From 22f9e585d9cef0b6f6da66be8b636117e1d82f94 Mon Sep 17 00:00:00 2001 From: Jonas Jesus Date: Tue, 5 May 2026 06:28:49 -0300 Subject: [PATCH] feat(google-official): wrap BigQuery / GKE / Maps so they actually work in mesh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three `google-*-official` MCPs were registered as bare URLs pointing at Google's MCP endpoints. They couldn't be installed in mesh because Google doesn't support Dynamic Client Registration (RFC 7591) — the same gap that motivated the google-workspace wrapper. This PR: - Extracts the proxy / json-schema-to-zod / wrap-tool helpers from google-workspace into a new shared module `@decocms/mcps-shared/google-mcp`. - Refactors google-workspace to consume the shared helpers (no behavior change; snapshots gain a `url` field, TOOLS.md wording slightly tweaked). - Adds `google-bigquery-official` (6 tools, scope `bigquery`), `google-gke-official` (23 tools, scope `container`) and `google-maps-official` (3 tools, scope `maps-platform.mapstools`) as full wrapper MCPs: server/main.ts with `createGoogleOAuth`, snapshot codegen, dist build, deploy.json entries, TOOLS.md catalog. - Flips `mesh_unlisted` to `false` on the three apps so they show up in the registry, and points their connection URL at the new workers (`sites-google-{name}-official.decocache.com/mcp`). Each wrapper needs the same `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` secrets in K8s (per-MCP) and the redirect URIs added to the OAuth client. Co-Authored-By: Claude Opus 4.7 --- bun.lock | 78 + deploy.json | 27 + google-bigquery-official/README.md | 10 +- google-bigquery-official/TOOLS.md | 18 + google-bigquery-official/app.json | 4 +- google-bigquery-official/package.json | 32 + google-bigquery-official/server/constants.ts | 15 + google-bigquery-official/server/lib/env.ts | 11 + google-bigquery-official/server/main.ts | 21 + .../server/scripts/generate-tools.ts | 16 + .../server/tools/generated/_index.json | 15 + .../server/tools/generated/bigquery.json | 2807 +++++++++++++++++ .../server/tools/index.ts | 9 + google-bigquery-official/shared/deco.gen.ts | 7 + google-bigquery-official/tsconfig.json | 26 + google-gke-official/README.md | 10 +- google-gke-official/TOOLS.md | 35 + google-gke-official/app.json | 4 +- google-gke-official/package.json | 32 + google-gke-official/server/constants.ts | 15 + google-gke-official/server/lib/env.ts | 9 + google-gke-official/server/main.ts | 21 + .../server/scripts/generate-tools.ts | 16 + .../server/tools/generated/_index.json | 32 + .../server/tools/generated/gke.json | 1825 +++++++++++ google-gke-official/server/tools/index.ts | 9 + google-gke-official/shared/deco.gen.ts | 7 + google-gke-official/tsconfig.json | 26 + google-maps-official/README.md | 10 +- google-maps-official/TOOLS.md | 15 + google-maps-official/app.json | 4 +- google-maps-official/package.json | 32 + google-maps-official/server/constants.ts | 15 + google-maps-official/server/lib/env.ts | 9 + google-maps-official/server/main.ts | 21 + .../server/scripts/generate-tools.ts | 16 + .../server/tools/generated/_index.json | 12 + .../server/tools/generated/maps.json | 957 ++++++ google-maps-official/server/tools/index.ts | 9 + google-maps-official/shared/deco.gen.ts | 7 + google-maps-official/tsconfig.json | 26 + google-workspace/TOOLS.md | 4 +- google-workspace/server/constants.ts | 8 +- google-workspace/server/lib/wrap-tool.ts | 45 - .../server/scripts/generate-tools.ts | 174 +- .../server/tools/generated/calendar.json | 1 + .../server/tools/generated/chat.json | 1 + .../server/tools/generated/drive.json | 1 + .../server/tools/generated/gmail.json | 1 + .../server/tools/generated/people.json | 1 + google-workspace/server/tools/index.ts | 27 +- package.json | 3 + shared/google-mcp/generate-snapshot.ts | 202 ++ shared/google-mcp/index.ts | 23 + .../google-mcp}/json-schema-to-zod.ts | 4 - .../google-mcp/proxy.ts | 9 +- shared/google-mcp/wrap-tool.ts | 78 + shared/package.json | 2 + 58 files changed, 6606 insertions(+), 248 deletions(-) create mode 100644 google-bigquery-official/TOOLS.md create mode 100644 google-bigquery-official/package.json create mode 100644 google-bigquery-official/server/constants.ts create mode 100644 google-bigquery-official/server/lib/env.ts create mode 100644 google-bigquery-official/server/main.ts create mode 100644 google-bigquery-official/server/scripts/generate-tools.ts create mode 100644 google-bigquery-official/server/tools/generated/_index.json create mode 100644 google-bigquery-official/server/tools/generated/bigquery.json create mode 100644 google-bigquery-official/server/tools/index.ts create mode 100644 google-bigquery-official/shared/deco.gen.ts create mode 100644 google-bigquery-official/tsconfig.json create mode 100644 google-gke-official/TOOLS.md create mode 100644 google-gke-official/package.json create mode 100644 google-gke-official/server/constants.ts create mode 100644 google-gke-official/server/lib/env.ts create mode 100644 google-gke-official/server/main.ts create mode 100644 google-gke-official/server/scripts/generate-tools.ts create mode 100644 google-gke-official/server/tools/generated/_index.json create mode 100644 google-gke-official/server/tools/generated/gke.json create mode 100644 google-gke-official/server/tools/index.ts create mode 100644 google-gke-official/shared/deco.gen.ts create mode 100644 google-gke-official/tsconfig.json create mode 100644 google-maps-official/TOOLS.md create mode 100644 google-maps-official/package.json create mode 100644 google-maps-official/server/constants.ts create mode 100644 google-maps-official/server/lib/env.ts create mode 100644 google-maps-official/server/main.ts create mode 100644 google-maps-official/server/scripts/generate-tools.ts create mode 100644 google-maps-official/server/tools/generated/_index.json create mode 100644 google-maps-official/server/tools/generated/maps.json create mode 100644 google-maps-official/server/tools/index.ts create mode 100644 google-maps-official/shared/deco.gen.ts create mode 100644 google-maps-official/tsconfig.json delete mode 100644 google-workspace/server/lib/wrap-tool.ts create mode 100644 shared/google-mcp/generate-snapshot.ts create mode 100644 shared/google-mcp/index.ts rename {google-workspace/server/lib => shared/google-mcp}/json-schema-to-zod.ts (89%) rename google-workspace/server/lib/mcp-proxy.ts => shared/google-mcp/proxy.ts (82%) create mode 100644 shared/google-mcp/wrap-tool.ts diff --git a/bun.lock b/bun.lock index 6945c2e6..427e941d 100644 --- a/bun.lock +++ b/bun.lock @@ -368,6 +368,20 @@ "typescript": "^5.7.2", }, }, + "google-bigquery-official": { + "name": "google-bigquery-official", + "version": "1.0.0", + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0", + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2", + }, + }, "google-calendar": { "name": "google-calendar", "version": "1.0.0", @@ -461,6 +475,20 @@ "typescript": "^5.7.2", }, }, + "google-gke-official": { + "name": "google-gke-official", + "version": "1.0.0", + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0", + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2", + }, + }, "google-gmail": { "name": "google-gmail", "version": "1.0.0", @@ -479,6 +507,20 @@ "wrangler": "^4.28.0", }, }, + "google-maps-official": { + "name": "google-maps-official", + "version": "1.0.0", + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0", + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2", + }, + }, "google-meet": { "name": "google-meet", "version": "1.0.0", @@ -2757,6 +2799,8 @@ "google-big-query": ["google-big-query@workspace:google-big-query"], + "google-bigquery-official": ["google-bigquery-official@workspace:google-bigquery-official"], + "google-calendar": ["google-calendar@workspace:google-calendar"], "google-calendar-sa": ["google-calendar-sa@workspace:google-calendar-sa"], @@ -2769,10 +2813,14 @@ "google-gemini": ["google-gemini@workspace:google-gemini"], + "google-gke-official": ["google-gke-official@workspace:google-gke-official"], + "google-gmail": ["google-gmail@workspace:google-gmail"], "google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], + "google-maps-official": ["google-maps-official@workspace:google-maps-official"], + "google-meet": ["google-meet@workspace:google-meet"], "google-search-console": ["google-search-console@workspace:google-search-console"], @@ -4141,6 +4189,10 @@ "google-big-query/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-bigquery-official/@decocms/runtime": ["@decocms/runtime@1.6.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.27.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-iVRp3yUvPd5x1nQ+WbsTOHA3KKBRMq6kGnJLoV1oyjIFm1uyxK69IihnYp1B49sQxlWAfPAJco3AZ8TuvF3T6A=="], + + "google-bigquery-official/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-calendar/@decocms/runtime": ["@decocms/runtime@1.3.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.27.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-xRFTV9gqrdsUCAkJ3xoBOeDGXps6moZeZB/NNL+XZ7LNa4qzfmL3BGWG42n8xbFyEpprfEfOtOD+tVgWhkPDxQ=="], "google-calendar/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -4169,6 +4221,10 @@ "google-gemini/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-gke-official/@decocms/runtime": ["@decocms/runtime@1.6.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.27.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-iVRp3yUvPd5x1nQ+WbsTOHA3KKBRMq6kGnJLoV1oyjIFm1uyxK69IihnYp1B49sQxlWAfPAJco3AZ8TuvF3T6A=="], + + "google-gke-official/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-gmail/@decocms/bindings": ["@decocms/bindings@1.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-olUAzaV/lAaBLW5Z+sedJtms3vbUOL9WYXOU2Wkh311Kk1LBOuQmbJrVNVZH4yj8j2UVWxFVPcjkT9gxAC0zdw=="], "google-gmail/@decocms/runtime": ["@decocms/runtime@1.6.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.27.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-iVRp3yUvPd5x1nQ+WbsTOHA3KKBRMq6kGnJLoV1oyjIFm1uyxK69IihnYp1B49sQxlWAfPAJco3AZ8TuvF3T6A=="], @@ -4179,6 +4235,10 @@ "google-gmail/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-maps-official/@decocms/runtime": ["@decocms/runtime@1.6.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.27.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-iVRp3yUvPd5x1nQ+WbsTOHA3KKBRMq6kGnJLoV1oyjIFm1uyxK69IihnYp1B49sQxlWAfPAJco3AZ8TuvF3T6A=="], + + "google-maps-official/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "google-meet/@decocms/runtime": ["@decocms/runtime@1.2.5", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.1.1", "@modelcontextprotocol/sdk": "1.25.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-0s02lfj/O7nTAc7FTmFsA+lZpUDnapjQHnRYrQXItLKrbJvjSnfoq5V8HA1Npv5HelBvsVk7QQHaW8pSN/l37w=="], "google-meet/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -4657,6 +4717,10 @@ "google-big-query/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], + "google-bigquery-official/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-olUAzaV/lAaBLW5Z+sedJtms3vbUOL9WYXOU2Wkh311Kk1LBOuQmbJrVNVZH4yj8j2UVWxFVPcjkT9gxAC0zdw=="], + + "google-bigquery-official/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "google-calendar-sa/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-olUAzaV/lAaBLW5Z+sedJtms3vbUOL9WYXOU2Wkh311Kk1LBOuQmbJrVNVZH4yj8j2UVWxFVPcjkT9gxAC0zdw=="], "google-calendar-sa/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], @@ -4683,10 +4747,18 @@ "google-gemini/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "google-gke-official/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-olUAzaV/lAaBLW5Z+sedJtms3vbUOL9WYXOU2Wkh311Kk1LBOuQmbJrVNVZH4yj8j2UVWxFVPcjkT9gxAC0zdw=="], + + "google-gke-official/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "google-gmail/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], "google-gmail/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "google-maps-official/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-olUAzaV/lAaBLW5Z+sedJtms3vbUOL9WYXOU2Wkh311Kk1LBOuQmbJrVNVZH4yj8j2UVWxFVPcjkT9gxAC0zdw=="], + + "google-maps-official/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "google-meet/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], "google-meet/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -5197,6 +5269,8 @@ "google-big-query/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-bigquery-official/@decocms/runtime/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "google-calendar-sa/@decocms/runtime/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], "google-calendar/@decocms/runtime/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], @@ -5209,6 +5283,10 @@ "google-gemini/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-gke-official/@decocms/runtime/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-maps-official/@decocms/runtime/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "google-meet/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], "google-search-console/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], diff --git a/deploy.json b/deploy.json index 93e224cd..b27be113 100644 --- a/deploy.json +++ b/deploy.json @@ -62,6 +62,33 @@ "shared/**" ] }, + "google-bigquery-official": { + "site": "google-bigquery-official", + "entrypoint": "./dist/server/main.js", + "platformName": "kubernetes-bun", + "watch": [ + "google-bigquery-official/**", + "shared/**" + ] + }, + "google-gke-official": { + "site": "google-gke-official", + "entrypoint": "./dist/server/main.js", + "platformName": "kubernetes-bun", + "watch": [ + "google-gke-official/**", + "shared/**" + ] + }, + "google-maps-official": { + "site": "google-maps-official", + "entrypoint": "./dist/server/main.js", + "platformName": "kubernetes-bun", + "watch": [ + "google-maps-official/**", + "shared/**" + ] + }, "google-calendar-sa": { "site": "google-calendar-sa", "entrypoint": "./dist/server/main.js", diff --git a/google-bigquery-official/README.md b/google-bigquery-official/README.md index f4d010f6..8364a501 100644 --- a/google-bigquery-official/README.md +++ b/google-bigquery-official/README.md @@ -1,6 +1,14 @@ # Google BigQuery MCP Server Official -This is the **official Google BigQuery MCP Server**, provided directly by the Google Cloud team for integration with BigQuery. +Thin wrapper around Google's official BigQuery MCP server (`bigquery.googleapis.com/mcp`). The wrapper holds the Google OAuth client credentials server-side and proxies JSON-RPC `tools/call` to the upstream — Google's MCP doesn't support Dynamic Client Registration, so this layer is needed to integrate it into mesh. + +See [`TOOLS.md`](./TOOLS.md) for the catalog (auto-generated via `bun run generate-tools`). Refresh after Google updates the upstream tools/list. + +The OAuth + proxy plumbing lives in `@decocms/mcps-shared/google-mcp` — same code path used by `google-workspace` and the other `google-*-official` wrappers. + +--- + +The upstream **Google BigQuery MCP Server** is provided directly by the Google Cloud team for integration with BigQuery. ## About Google BigQuery diff --git a/google-bigquery-official/TOOLS.md b/google-bigquery-official/TOOLS.md new file mode 100644 index 00000000..14a9cc35 --- /dev/null +++ b/google-bigquery-official/TOOLS.md @@ -0,0 +1,18 @@ +# Google BigQuery — Tool Catalog + +_Auto-generated by `bun run generate-tools`. Do not edit by hand._ + +6 tools across 1 backend, gated behind 1 OAuth scope. + +**Endpoint:** `https://bigquery.googleapis.com/mcp` + +**Scopes:** +- `https://www.googleapis.com/auth/bigquery` + +**Tools (6):** +- `list_dataset_ids` — List BigQuery dataset IDs in a Google Cloud project. +- `get_dataset_info` — Get metadata information about a BigQuery dataset. +- `list_table_ids` — List table ids in a BigQuery dataset. +- `get_table_info` — Get metadata information about a BigQuery table. +- `execute_sql_readonly` — Run a read-only SQL query in the project and return the result. Prefer this tool over +- `execute_sql` — Run a SQL query in the project and return the result. Prefer the `execute_sql_readonly` diff --git a/google-bigquery-official/app.json b/google-bigquery-official/app.json index 08135556..e3524721 100644 --- a/google-bigquery-official/app.json +++ b/google-bigquery-official/app.json @@ -4,7 +4,7 @@ "friendlyName": "Google BigQuery", "connection": { "type": "HTTP", - "url": "https://bigquery.googleapis.com/mcp" + "url": "https://sites-google-bigquery-official.decocache.com/mcp" }, "description": "Analyze massive datasets with Google BigQuery serverless data warehouse. Run SQL queries, manage datasets, and perform analytics through natural language.", "icon": "https://www.gstatic.com/images/branding/product/2x/bigquery_64dp.png", @@ -12,7 +12,7 @@ "metadata": { "categories": ["Data Analysis"], "official": true, - "mesh_unlisted": true, + "mesh_unlisted": false, "tags": ["data-warehouse", "sql", "analytics", "bigdata", "google-cloud", "machine-learning", "bi", "data-science", "petabyte-scale"], "short_description": "Analyze massive datasets with Google BigQuery serverless data warehouse", "mesh_description": "Google BigQuery is a fully managed, serverless data warehouse that enables super-fast SQL queries using the processing power of Google's infrastructure. This official MCP provides natural language access to BigQuery's powerful analytics capabilities, allowing you to analyze petabytes of data in seconds. Create and manage datasets, tables, and views with schema definition and partitioning strategies. Run complex SQL queries with support for standard SQL, geographic functions, machine learning functions, and user-defined functions (UDFs). Load data from various sources including Cloud Storage, Cloud SQL, Sheets, and streaming inserts. Execute federated queries that can join BigQuery data with external data sources like Cloud Storage, Bigtable, or Cloud SQL. Use BigQuery ML to create and execute machine learning models directly in SQL for tasks like classification, regression, forecasting, and recommendation. Access real-time analytics with streaming inserts and automatic table updates. Optimize costs with automatic query caching, materialized views, and partitioned tables. Monitor query performance with execution plans, slot usage, and cost estimates. Set up scheduled queries for automated data refreshes, configure access controls with IAM policies, and export results to various formats for further analysis or visualization." diff --git a/google-bigquery-official/package.json b/google-bigquery-official/package.json new file mode 100644 index 00000000..ad037895 --- /dev/null +++ b/google-bigquery-official/package.json @@ -0,0 +1,32 @@ +{ + "name": "google-bigquery-official", + "version": "1.0.0", + "description": "Wrapper around Google's official BigQuery MCP server (bigquery.googleapis.com/mcp)", + "private": true, + "type": "module", + "scripts": { + "dev": "bun run --hot server/main.ts", + "build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js", + "build": "bun run build:server", + "publish": "cat app.json | deco registry publish -w /shared/deco -y", + "check": "tsc --noEmit", + "generate-tools": "bun run server/scripts/generate-tools.ts" + }, + "exports": { + "./tools": "./server/tools/index.ts", + "./constants": "./server/constants.ts" + }, + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0" + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/google-bigquery-official/server/constants.ts b/google-bigquery-official/server/constants.ts new file mode 100644 index 00000000..1a8d2d73 --- /dev/null +++ b/google-bigquery-official/server/constants.ts @@ -0,0 +1,15 @@ +import snapshot from "./tools/generated/bigquery.json" with { type: "json" }; +import type { BackendToolDefinition } from "@decocms/mcps-shared/google-mcp"; + +export const BACKEND_URL = "https://bigquery.googleapis.com/mcp"; + +export interface BackendSnapshot { + service: string; + url: string; + scopes: string[]; + tools: BackendToolDefinition[]; +} + +export const TOOL_SNAPSHOT = snapshot as BackendSnapshot; + +export const SCOPES: string[] = TOOL_SNAPSHOT.scopes; diff --git a/google-bigquery-official/server/lib/env.ts b/google-bigquery-official/server/lib/env.ts new file mode 100644 index 00000000..51400b94 --- /dev/null +++ b/google-bigquery-official/server/lib/env.ts @@ -0,0 +1,11 @@ +import type { Env } from "../../shared/deco.gen.ts"; + +export const getGoogleAccessToken = (env: Env): string => { + const authorization = env.MESH_REQUEST_CONTEXT?.authorization; + if (!authorization) { + throw new Error( + "Not authenticated. Please authorize Google BigQuery first.", + ); + } + return authorization; +}; diff --git a/google-bigquery-official/server/main.ts b/google-bigquery-official/server/main.ts new file mode 100644 index 00000000..024c1b75 --- /dev/null +++ b/google-bigquery-official/server/main.ts @@ -0,0 +1,21 @@ +import { withRuntime } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { serve } from "@decocms/mcps-shared/serve"; +import { createGoogleOAuth } from "@decocms/mcps-shared/google-oauth"; + +import { tools } from "./tools/index.ts"; +import { SCOPES } from "./constants.ts"; +import { type Env, StateSchema } from "../shared/deco.gen.ts"; + +export type { Env }; + +const runtime = withRuntime({ + configuration: { + scopes: [], + state: StateSchema, + }, + tools: (env: Env) => tools.map((createTool) => createTool(env)), + oauth: createGoogleOAuth({ scopes: SCOPES }), +}); + +serve(runtime.fetch); diff --git a/google-bigquery-official/server/scripts/generate-tools.ts b/google-bigquery-official/server/scripts/generate-tools.ts new file mode 100644 index 00000000..ec3dd6f1 --- /dev/null +++ b/google-bigquery-official/server/scripts/generate-tools.ts @@ -0,0 +1,16 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { generateSnapshots } from "@decocms/mcps-shared/google-mcp/generate-snapshot"; +import { BACKEND_URL } from "../constants.ts"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const OUT_DIR = join(HERE, "..", "tools", "generated"); +const PACKAGE_ROOT = join(HERE, "..", ".."); +const TOOLS_MD_PATH = join(PACKAGE_ROOT, "TOOLS.md"); + +await generateSnapshots({ + title: "Google BigQuery — Tool Catalog", + outDir: OUT_DIR, + toolsMarkdownPath: TOOLS_MD_PATH, + backends: [{ service: "bigquery", url: BACKEND_URL }], +}); diff --git a/google-bigquery-official/server/tools/generated/_index.json b/google-bigquery-official/server/tools/generated/_index.json new file mode 100644 index 00000000..3792061c --- /dev/null +++ b/google-bigquery-official/server/tools/generated/_index.json @@ -0,0 +1,15 @@ +{ + "bigquery": { + "scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "toolNames": [ + "list_dataset_ids", + "get_dataset_info", + "list_table_ids", + "get_table_info", + "execute_sql_readonly", + "execute_sql" + ] + } +} diff --git a/google-bigquery-official/server/tools/generated/bigquery.json b/google-bigquery-official/server/tools/generated/bigquery.json new file mode 100644 index 00000000..ba8e9997 --- /dev/null +++ b/google-bigquery-official/server/tools/generated/bigquery.json @@ -0,0 +1,2807 @@ +{ + "service": "bigquery", + "url": "https://bigquery.googleapis.com/mcp", + "scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "tools": [ + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "List BigQuery dataset IDs in a Google Cloud project.", + "inputSchema": { + "description": "Request for a list of datasets in a project.", + "properties": { + "projectId": { + "description": "Required. Project ID of the dataset request.", + "type": "string" + } + }, + "required": [ + "projectId" + ], + "type": "object" + }, + "name": "list_dataset_ids", + "outputSchema": { + "$defs": { + "ListFormatDataset": { + "description": "Represents a dataset in BigQuery.", + "properties": { + "friendlyName": { + "description": "An alternate name for the dataset. The friendly name is purely decorative in nature. This can be useful to derive additional information about the dataset.", + "type": "string" + }, + "id": { + "description": "The ID of the dataset.", + "type": "string" + }, + "location": { + "description": "The geographic location where the dataset resides.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for a list of datasets.", + "properties": { + "datasets": { + "description": "The datasets that matched the request.", + "items": { + "$ref": "#/$defs/ListFormatDataset" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Get metadata information about a BigQuery dataset.", + "inputSchema": { + "description": "Request for a dataset.", + "properties": { + "datasetId": { + "description": "Required. Dataset ID of the dataset request.", + "type": "string" + }, + "projectId": { + "description": "Required. Project ID of the dataset request.", + "type": "string" + } + }, + "required": [ + "projectId", + "datasetId" + ], + "type": "object" + }, + "name": "get_dataset_info", + "outputSchema": { + "$defs": { + "Access": { + "description": "An object that defines dataset access for an entity.", + "properties": { + "condition": { + "$ref": "#/$defs/Expr", + "description": "Optional. condition for the binding. If CEL expression in this field is true, this access binding will be considered" + }, + "dataset": { + "$ref": "#/$defs/DatasetAccessEntry", + "description": "[Pick one] A grant authorizing all resources of a particular type in a particular dataset access to this dataset. Only views are supported for now. The role field is not required when this field is set. If that dataset is deleted and re-created, its access needs to be granted again via an update operation." + }, + "domain": { + "description": "[Pick one] A domain to grant access to. Any users signed in with the domain specified will be granted the specified access. Example: \"example.com\". Maps to IAM policy member \"domain:DOMAIN\".", + "type": "string" + }, + "groupByEmail": { + "description": "[Pick one] An email address of a Google Group to grant access to. Maps to IAM policy member \"group:GROUP\".", + "type": "string" + }, + "iamMember": { + "description": "[Pick one] Some other type of member that appears in the IAM Policy but isn't a user, group, domain, or special group.", + "type": "string" + }, + "role": { + "description": "An IAM role ID that should be granted to the user, group, or domain specified in this access entry. The following legacy mappings will be applied: * `OWNER`: `roles/bigquery.dataOwner` * `WRITER`: `roles/bigquery.dataEditor` * `READER`: `roles/bigquery.dataViewer` This field will accept any of the above formats, but will return only the legacy format. For example, if you set this field to \"roles/bigquery.dataOwner\", it will be returned back as \"OWNER\".", + "type": "string" + }, + "routine": { + "$ref": "#/$defs/RoutineReference", + "description": "[Pick one] A routine from a different dataset to grant access to. Queries executed against that routine will have read access to views/tables/routines in this dataset. Only UDF is supported for now. The role field is not required when this field is set. If that routine is updated by any user, access to the routine needs to be granted again via an update operation." + }, + "specialGroup": { + "description": "[Pick one] A special group to grant access to. Possible values include: * projectOwners: Owners of the enclosing project. * projectReaders: Readers of the enclosing project. * projectWriters: Writers of the enclosing project. * allAuthenticatedUsers: All authenticated BigQuery users. Maps to similarly-named IAM members.", + "type": "string" + }, + "userByEmail": { + "description": "[Pick one] An email address of a user to grant access to. For example: fred@example.com. Maps to IAM policy member \"user:EMAIL\" or \"serviceAccount:EMAIL\".", + "type": "string" + }, + "view": { + "$ref": "#/$defs/TableReference", + "description": "[Pick one] A view from a different dataset to grant access to. Queries executed against that view will have read access to views/tables/routines in this dataset. The role field is not required when this field is set. If that view is updated by any user, access to the view needs to be granted again via an update operation." + } + }, + "type": "object" + }, + "DatasetAccessEntry": { + "description": "Grants all resources of particular types in a particular dataset read access to the current dataset. Similar to how individually authorized views work, updates to any resource granted through its dataset (including creation of new resources) requires read permission to referenced resources, plus write permission to the authorizing dataset.", + "properties": { + "dataset": { + "$ref": "#/$defs/DatasetReference", + "description": "The dataset this entry applies to" + }, + "targetTypes": { + "description": "Which resources in the dataset this entry applies to. Currently, only views are supported, but additional target types may be added in the future.", + "items": { + "enum": [ + "TARGET_TYPE_UNSPECIFIED", + "VIEWS", + "ROUTINES" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Do not use. You must set a target type explicitly.", + "This entry applies to views in the dataset.", + "This entry applies to routines in the dataset." + ] + }, + "type": "array" + } + }, + "type": "object" + }, + "DatasetReference": { + "description": "Identifier for a dataset.", + "properties": { + "datasetId": { + "description": "Required. A unique ID for this dataset, without the project name. The ID must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_). The maximum length is 1,024 characters.", + "type": "string" + }, + "projectId": { + "description": "Optional. The ID of the project containing this dataset.", + "type": "string" + } + }, + "type": "object" + }, + "EncryptionConfiguration": { + "description": "Configuration for Cloud KMS encryption settings.", + "properties": { + "kmsKeyName": { + "description": "Optional. Describes the Cloud KMS encryption key that will be used to protect destination BigQuery table. The BigQuery Service Account associated with your project requires access to this encryption key.", + "type": "string" + } + }, + "type": "object" + }, + "Expr": { + "description": "Represents a textual expression in the Common Expression Language (CEL) syntax. CEL is a C-like expression language. The syntax and semantics of CEL are documented at https://github.com/google/cel-spec. Example (Comparison): title: \"Summary size limit\" description: \"Determines if a summary is less than 100 chars\" expression: \"document.summary.size() < 100\" Example (Equality): title: \"Requestor is owner\" description: \"Determines if requestor is the document owner\" expression: \"document.owner == request.auth.claims.email\" Example (Logic): title: \"Public documents\" description: \"Determine whether the document should be publicly visible\" expression: \"document.type != 'private' && document.type != 'internal'\" Example (Data Manipulation): title: \"Notification string\" description: \"Create a notification string with a timestamp.\" expression: \"'New message received at ' + string(document.create_time)\" The exact variables and functions that may be referenced within an expression are determined by the service that evaluates it. See the service documentation for additional information.", + "properties": { + "description": { + "description": "Optional. Description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI.", + "type": "string" + }, + "expression": { + "description": "Textual representation of an expression in Common Expression Language syntax.", + "type": "string" + }, + "location": { + "description": "Optional. String indicating the location of the expression for error reporting, e.g. a file name and a position in the file.", + "type": "string" + }, + "title": { + "description": "Optional. Title for the expression, i.e. a short string describing its purpose. This can be used e.g. in UIs which allow to enter the expression.", + "type": "string" + } + }, + "type": "object" + }, + "ExternalCatalogDatasetOptions": { + "description": "Options defining open source compatible datasets living in the BigQuery catalog. Contains metadata of open source database, schema, or namespace represented by the current dataset.", + "properties": { + "defaultStorageLocationUri": { + "description": "Optional. The storage location URI for all tables in the dataset. Equivalent to hive metastore's database locationUri. Maximum length of 1024 characters.", + "type": "string" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional. A map of key value pairs defining the parameters and properties of the open source schema. Maximum size of 2MiB.", + "type": "object" + } + }, + "type": "object" + }, + "ExternalDatasetReference": { + "description": "Configures the access a dataset defined in an external metadata storage.", + "properties": { + "connection": { + "description": "Required. The connection id that is used to access the external_source. Format: projects/{project_id}/locations/{location_id}/connections/{connection_id}", + "type": "string" + }, + "externalSource": { + "description": "Required. External source that backs this dataset.", + "type": "string" + } + }, + "type": "object" + }, + "GcpTag": { + "description": "A global tag managed by Resource Manager. https://cloud.google.com/iam/docs/tags-access-control#definitions", + "properties": { + "tagKey": { + "description": "Required. The namespaced friendly name of the tag key, e.g. \"12345/environment\" where 12345 is org id.", + "type": "string" + }, + "tagValue": { + "description": "Required. The friendly short name of the tag value, e.g. \"production\".", + "type": "string" + } + }, + "type": "object" + }, + "LinkedDatasetMetadata": { + "description": "Metadata about the Linked Dataset.", + "properties": { + "linkState": { + "description": "Output only. Specifies whether Linked Dataset is currently in a linked state or not.", + "enum": [ + "LINK_STATE_UNSPECIFIED", + "LINKED", + "UNLINKED" + ], + "readOnly": true, + "type": "string", + "x-google-enum-descriptions": [ + "The default value. Default to the LINKED state.", + "Normal Linked Dataset state. Data is queryable via the Linked Dataset.", + "Data publisher or owner has unlinked this Linked Dataset. It means you can no longer query or see the data in the Linked Dataset." + ] + } + }, + "type": "object" + }, + "LinkedDatasetSource": { + "description": "A dataset source type which refers to another BigQuery dataset.", + "properties": { + "sourceDataset": { + "$ref": "#/$defs/DatasetReference", + "description": "The source dataset reference contains project numbers and not project ids." + } + }, + "type": "object" + }, + "RestrictionConfig": { + "properties": { + "type": { + "description": "Output only. Specifies the type of dataset/table restriction.", + "enum": [ + "RESTRICTION_TYPE_UNSPECIFIED", + "RESTRICTED_DATA_EGRESS" + ], + "readOnly": true, + "type": "string", + "x-google-enum-descriptions": [ + "Should never be used.", + "Restrict data egress. See [Data egress](https://cloud.google.com/bigquery/docs/analytics-hub-introduction#data_egress) for more details." + ] + } + }, + "type": "object" + }, + "RoutineReference": { + "description": "Id path of a routine.", + "properties": { + "datasetId": { + "description": "Required. The ID of the dataset containing this routine.", + "type": "string" + }, + "projectId": { + "description": "Required. The ID of the project containing this routine.", + "type": "string" + }, + "routineId": { + "description": "Required. The ID of the routine. The ID must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_). The maximum length is 256 characters.", + "type": "string" + } + }, + "type": "object" + }, + "TableReference": { + "properties": { + "datasetId": { + "description": "Required. The ID of the dataset containing this table.", + "type": "string" + }, + "projectId": { + "description": "Required. The ID of the project containing this table.", + "type": "string" + }, + "tableId": { + "description": "Required. The ID of the table. The ID can contain Unicode characters in category L (letter), M (mark), N (number), Pc (connector, including underscore), Pd (dash), and Zs (space). For more information, see [General Category](https://wikipedia.org/wiki/Unicode_character_property#General_Category). The maximum length is 1,024 characters. Certain operations allow suffixing of the table ID with a partition decorator, such as `sample_table$20190123`.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Represents a BigQuery dataset.", + "properties": { + "access": { + "description": "Optional. An array of objects that define dataset access for one or more entities. You can set this property when inserting or updating a dataset in order to control who is allowed to access the data. If unspecified at dataset creation time, BigQuery adds default dataset access for the following entities: access.specialGroup: projectReaders; access.role: READER; access.specialGroup: projectWriters; access.role: WRITER; access.specialGroup: projectOwners; access.role: OWNER; access.userByEmail: [dataset creator email]; access.role: OWNER; If you patch a dataset, then this field is overwritten by the patched dataset's access field. To add entities, you must supply the entire existing access array in addition to any new entities that you want to add.", + "items": { + "$ref": "#/$defs/Access" + }, + "type": "array" + }, + "catalogSource": { + "description": "Output only. The origin of the dataset, one of: * (Unset) - Native BigQuery Dataset * BIGLAKE - Dataset is backed by a namespace stored natively in Biglake", + "readOnly": true, + "type": "string" + }, + "creationTime": { + "description": "Output only. The time when this dataset was created, in milliseconds since the epoch.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "datasetReference": { + "$ref": "#/$defs/DatasetReference", + "description": "Required. A reference that identifies the dataset." + }, + "defaultCollation": { + "description": "Optional. Defines the default collation specification of future tables created in the dataset. If a table is created in this dataset without table-level default collation, then the table inherits the dataset default collation, which is applied to the string fields that do not have explicit collation specified. A change to this field affects only tables created afterwards, and does not alter the existing tables. The following values are supported: * 'und:ci': undetermined locale, case insensitive. * '': empty string. Default to case-sensitive behavior.", + "type": "string" + }, + "defaultEncryptionConfiguration": { + "$ref": "#/$defs/EncryptionConfiguration", + "description": "The default encryption key for all tables in the dataset. After this property is set, the encryption key of all newly-created tables in the dataset is set to this value unless the table creation request or query explicitly overrides the key." + }, + "defaultPartitionExpirationMs": { + "description": "This default partition expiration, expressed in milliseconds. When new time-partitioned tables are created in a dataset where this property is set, the table will inherit this value, propagated as the `TimePartitioning.expirationMs` property on the new table. If you set `TimePartitioning.expirationMs` explicitly when creating a table, the `defaultPartitionExpirationMs` of the containing dataset is ignored. When creating a partitioned table, if `defaultPartitionExpirationMs` is set, the `defaultTableExpirationMs` value is ignored and the table will not be inherit a table expiration deadline.", + "format": "int64", + "type": "string" + }, + "defaultRoundingMode": { + "description": "Optional. Defines the default rounding mode specification of new tables created within this dataset. During table creation, if this field is specified, the table within this dataset will inherit the default rounding mode of the dataset. Setting the default rounding mode on a table overrides this option. Existing tables in the dataset are unaffected. If columns are defined during that table creation, they will immediately inherit the table's default rounding mode, unless otherwise specified.", + "enum": [ + "ROUNDING_MODE_UNSPECIFIED", + "ROUND_HALF_AWAY_FROM_ZERO", + "ROUND_HALF_EVEN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO.", + "ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2", + "ROUND_HALF_EVEN rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2" + ] + }, + "defaultTableExpirationMs": { + "description": "Optional. The default lifetime of all tables in the dataset, in milliseconds. The minimum lifetime value is 3600000 milliseconds (one hour). To clear an existing default expiration with a PATCH request, set to 0. Once this property is set, all newly-created tables in the dataset will have an expirationTime property set to the creation time plus the value in this property, and changing the value will only affect new tables, not existing ones. When the expirationTime for a given table is reached, that table will be deleted automatically. If a table's expirationTime is modified or removed before the table expires, or if you provide an explicit expirationTime when creating a table, that value takes precedence over the default expiration time indicated by this property.", + "format": "int64", + "type": "string" + }, + "description": { + "description": "Optional. A user-friendly description of the dataset.", + "type": "string" + }, + "etag": { + "description": "Output only. A hash of the resource.", + "readOnly": true, + "type": "string" + }, + "externalCatalogDatasetOptions": { + "$ref": "#/$defs/ExternalCatalogDatasetOptions", + "description": "Optional. Options defining open source compatible datasets living in the BigQuery catalog. Contains metadata of open source database, schema or namespace represented by the current dataset." + }, + "externalDatasetReference": { + "$ref": "#/$defs/ExternalDatasetReference", + "description": "Optional. Reference to a read-only external dataset defined in data catalogs outside of BigQuery. Filled out when the dataset type is EXTERNAL." + }, + "friendlyName": { + "description": "Optional. A descriptive name for the dataset.", + "type": "string" + }, + "id": { + "description": "Output only. The fully-qualified unique name of the dataset in the format projectId:datasetId. The dataset name without the project name is given in the datasetId field. When creating a new dataset, leave this field blank, and instead specify the datasetId field.", + "readOnly": true, + "type": "string" + }, + "isCaseInsensitive": { + "description": "Optional. TRUE if the dataset and its table names are case-insensitive, otherwise FALSE. By default, this is FALSE, which means the dataset and its table names are case-sensitive. This field does not affect routine references.", + "type": "boolean" + }, + "kind": { + "default": "bigquery#dataset", + "description": "Output only. The resource type.", + "readOnly": true, + "type": "string" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "description": "The labels associated with this dataset. You can use these to organize and group your datasets. You can set this property when inserting or updating a dataset. See [Creating and Updating Dataset Labels](https://cloud.google.com/bigquery/docs/creating-managing-labels#creating_and_updating_dataset_labels) for more information.", + "type": "object" + }, + "lastModifiedTime": { + "description": "Output only. The date when this dataset was last modified, in milliseconds since the epoch.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "linkedDatasetMetadata": { + "$ref": "#/$defs/LinkedDatasetMetadata", + "description": "Output only. Metadata about the LinkedDataset. Filled out when the dataset type is LINKED.", + "readOnly": true + }, + "linkedDatasetSource": { + "$ref": "#/$defs/LinkedDatasetSource", + "description": "Optional. The source dataset reference when the dataset is of type LINKED. For all other dataset types it is not set. This field cannot be updated once it is set. Any attempt to update this field using Update and Patch API Operations will be ignored." + }, + "location": { + "description": "The geographic location where the dataset should reside. See https://cloud.google.com/bigquery/docs/locations for supported locations.", + "type": "string" + }, + "maxTimeTravelHours": { + "description": "Optional. Defines the time travel window in hours. The value can be from 48 to 168 hours (2 to 7 days). The default value is 168 hours if this is not set.", + "format": "int64", + "type": "string" + }, + "resourceTags": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional. The [tags](https://cloud.google.com/bigquery/docs/tags) attached to this dataset. Tag keys are globally unique. Tag key is expected to be in the namespaced format, for example \"123456789012/environment\" where 123456789012 is the ID of the parent organization or project resource for this tag key. Tag value is expected to be the short name, for example \"Production\". See [Tag definitions](https://cloud.google.com/iam/docs/tags-access-control#definitions) for more details.", + "type": "object" + }, + "restrictions": { + "$ref": "#/$defs/RestrictionConfig", + "description": "Optional. Output only. Restriction config for all tables and dataset. If set, restrict certain accesses on the dataset and all its tables based on the config. See [Data egress](https://cloud.google.com/bigquery/docs/analytics-hub-introduction#data_egress) for more details.", + "readOnly": true + }, + "satisfiesPzi": { + "description": "Output only. Reserved for future use.", + "readOnly": true, + "type": "boolean" + }, + "satisfiesPzs": { + "description": "Output only. Reserved for future use.", + "readOnly": true, + "type": "boolean" + }, + "selfLink": { + "description": "Output only. A URL that can be used to access the resource again. You can use this URL in Get or Update requests to the resource.", + "readOnly": true, + "type": "string" + }, + "storageBillingModel": { + "description": "Optional. Updates storage_billing_model for the dataset.", + "enum": [ + "STORAGE_BILLING_MODEL_UNSPECIFIED", + "LOGICAL", + "PHYSICAL" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Value not set.", + "Billing for logical bytes.", + "Billing for physical bytes." + ] + }, + "tags": { + "deprecated": true, + "description": "Output only. Tags for the dataset. To provide tags as inputs, use the `resourceTags` field.", + "items": { + "$ref": "#/$defs/GcpTag" + }, + "readOnly": true, + "type": "array" + }, + "type": { + "description": "Output only. Same as `type` in `ListFormatDataset`. The type of the dataset, one of: * DEFAULT - only accessible by owner and authorized accounts, * PUBLIC - accessible by everyone, * LINKED - linked dataset, * EXTERNAL - dataset with definition in external metadata catalog, * BIGLAKE_ICEBERG - a Biglake dataset accessible through the Iceberg API, * BIGLAKE_HIVE - a Biglake dataset accessible through the Hive API.", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "List table ids in a BigQuery dataset.", + "inputSchema": { + "description": "Request for a list of tables in a dataset.", + "properties": { + "datasetId": { + "description": "Required. Dataset ID of the table request.", + "type": "string" + }, + "projectId": { + "description": "Required. Project ID of the table request.", + "type": "string" + } + }, + "required": [ + "projectId", + "datasetId" + ], + "type": "object" + }, + "name": "list_table_ids", + "outputSchema": { + "$defs": { + "ListFormatTable": { + "description": "Represents a table in BigQuery.", + "properties": { + "id": { + "description": "The ID of the table.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for a list of tables.", + "properties": { + "tables": { + "description": "The tables that matched the request.", + "items": { + "$ref": "#/$defs/ListFormatTable" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Get metadata information about a BigQuery table.", + "inputSchema": { + "description": "Request for a table.", + "properties": { + "datasetId": { + "description": "Required. Dataset ID of the table request.", + "type": "string" + }, + "projectId": { + "description": "Required. Project ID of the table request.", + "type": "string" + }, + "tableId": { + "description": "Required. Table ID of the table request.", + "type": "string" + } + }, + "required": [ + "projectId", + "datasetId", + "tableId" + ], + "type": "object" + }, + "name": "get_table_info", + "outputSchema": { + "$defs": { + "AggregationThresholdPolicy": { + "description": "Represents privacy policy associated with \"aggregation threshold\" method.", + "properties": { + "privacyUnitColumns": { + "description": "Optional. The privacy unit column(s) associated with this policy. For now, only one column per data source object (table, view) is allowed as a privacy unit column. Representing as a repeated field in metadata for extensibility to multiple columns in future. Duplicates and Repeated struct fields are not allowed. For nested fields, use dot notation (\"outer.inner\")", + "items": { + "type": "string" + }, + "type": "array" + }, + "threshold": { + "description": "Optional. The threshold for the \"aggregation threshold\" policy.", + "format": "int64", + "type": "string" + } + }, + "type": "object" + }, + "AvroOptions": { + "description": "Options for external data sources.", + "properties": { + "useAvroLogicalTypes": { + "description": "Optional. If sourceFormat is set to \"AVRO\", indicates whether to interpret logical types as the corresponding BigQuery data type (for example, TIMESTAMP), instead of using the raw type (for example, INTEGER).", + "type": "boolean" + } + }, + "type": "object" + }, + "BigLakeConfiguration": { + "description": "Configuration for BigQuery tables for Apache Iceberg (formerly BigLake managed tables.)", + "properties": { + "connectionId": { + "description": "Optional. The connection specifying the credentials to be used to read and write to external storage, such as Cloud Storage. The connection_id can have the form `{project}.{location}.{connection_id}` or `projects/{project}/locations/{location}/connections/{connection_id}\".", + "type": "string" + }, + "fileFormat": { + "description": "Optional. The file format the table data is stored in.", + "enum": [ + "FILE_FORMAT_UNSPECIFIED", + "PARQUET" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Default Value.", + "Apache Parquet format." + ] + }, + "storageUri": { + "description": "Optional. The fully qualified location prefix of the external folder where table data is stored. The '*' wildcard character is not allowed. The URI should be in the format `gs://bucket/path_to_table/`", + "type": "string" + }, + "tableFormat": { + "description": "Optional. The table format the metadata only snapshots are stored in.", + "enum": [ + "TABLE_FORMAT_UNSPECIFIED", + "ICEBERG" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Default Value.", + "Apache Iceberg format." + ] + } + }, + "type": "object" + }, + "BigtableColumn": { + "description": "Information related to a Bigtable column.", + "properties": { + "encoding": { + "description": "Optional. The encoding of the values when the type is not STRING. Acceptable encoding values are: TEXT - indicates values are alphanumeric text strings. BINARY - indicates values are encoded using HBase Bytes.toBytes family of functions. PROTO_BINARY - indicates values are encoded using serialized proto messages. This can only be used in combination with JSON type. 'encoding' can also be set at the column family level. However, the setting at this level takes precedence if 'encoding' is set at both levels.", + "type": "string" + }, + "fieldName": { + "description": "Optional. If the qualifier is not a valid BigQuery field identifier i.e. does not match a-zA-Z*, a valid identifier must be provided as the column field name and is used as field name in queries.", + "type": "string" + }, + "onlyReadLatest": { + "description": "Optional. If this is set, only the latest version of value in this column are exposed. 'onlyReadLatest' can also be set at the column family level. However, the setting at this level takes precedence if 'onlyReadLatest' is set at both levels.", + "type": "boolean" + }, + "protoConfig": { + "$ref": "#/$defs/BigtableProtoConfig", + "description": "Optional. Protobuf-specific configurations, only takes effect when the encoding is PROTO_BINARY." + }, + "qualifierEncoded": { + "description": "[Required] Qualifier of the column. Columns in the parent column family that has this exact qualifier are exposed as `.` field. If the qualifier is valid UTF-8 string, it can be specified in the qualifier_string field. Otherwise, a base-64 encoded value must be set to qualifier_encoded. The column field name is the same as the column qualifier. However, if the qualifier is not a valid BigQuery field identifier i.e. does not match a-zA-Z*, a valid identifier must be provided as field_name.", + "format": "byte", + "type": "string" + }, + "qualifierString": { + "description": "Qualifier string.", + "type": "string" + }, + "type": { + "description": "Optional. The type to convert the value in cells of this column. The values are expected to be encoded using HBase Bytes.toBytes function when using the BINARY encoding value. Following BigQuery types are allowed (case-sensitive): * BYTES * STRING * INTEGER * FLOAT * BOOLEAN * JSON Default type is BYTES. 'type' can also be set at the column family level. However, the setting at this level takes precedence if 'type' is set at both levels.", + "type": "string" + } + }, + "type": "object" + }, + "BigtableColumnFamily": { + "description": "Information related to a Bigtable column family.", + "properties": { + "columns": { + "description": "Optional. Lists of columns that should be exposed as individual fields as opposed to a list of (column name, value) pairs. All columns whose qualifier matches a qualifier in this list can be accessed as `.`. Other columns can be accessed as a list through the `.Column` field.", + "items": { + "$ref": "#/$defs/BigtableColumn" + }, + "type": "array" + }, + "encoding": { + "description": "Optional. The encoding of the values when the type is not STRING. Acceptable encoding values are: TEXT - indicates values are alphanumeric text strings. BINARY - indicates values are encoded using HBase Bytes.toBytes family of functions. PROTO_BINARY - indicates values are encoded using serialized proto messages. This can only be used in combination with JSON type. This can be overridden for a specific column by listing that column in 'columns' and specifying an encoding for it.", + "type": "string" + }, + "familyId": { + "description": "Identifier of the column family.", + "type": "string" + }, + "onlyReadLatest": { + "description": "Optional. If this is set only the latest version of value are exposed for all columns in this column family. This can be overridden for a specific column by listing that column in 'columns' and specifying a different setting for that column.", + "type": "boolean" + }, + "protoConfig": { + "$ref": "#/$defs/BigtableProtoConfig", + "description": "Optional. Protobuf-specific configurations, only takes effect when the encoding is PROTO_BINARY." + }, + "type": { + "description": "Optional. The type to convert the value in cells of this column family. The values are expected to be encoded using HBase Bytes.toBytes function when using the BINARY encoding value. Following BigQuery types are allowed (case-sensitive): * BYTES * STRING * INTEGER * FLOAT * BOOLEAN * JSON Default type is BYTES. This can be overridden for a specific column by listing that column in 'columns' and specifying a type for it.", + "type": "string" + } + }, + "type": "object" + }, + "BigtableOptions": { + "description": "Options specific to Google Cloud Bigtable data sources.", + "properties": { + "columnFamilies": { + "description": "Optional. List of column families to expose in the table schema along with their types. This list restricts the column families that can be referenced in queries and specifies their value types. You can use this list to do type conversions - see the 'type' field for more details. If you leave this list empty, all column families are present in the table schema and their values are read as BYTES. During a query only the column families referenced in that query are read from Bigtable.", + "items": { + "$ref": "#/$defs/BigtableColumnFamily" + }, + "type": "array" + }, + "ignoreUnspecifiedColumnFamilies": { + "description": "Optional. If field is true, then the column families that are not specified in columnFamilies list are not exposed in the table schema. Otherwise, they are read with BYTES type values. The default value is false.", + "type": "boolean" + }, + "outputColumnFamiliesAsJson": { + "description": "Optional. If field is true, then each column family will be read as a single JSON column. Otherwise they are read as a repeated cell structure containing timestamp/value tuples. The default value is false.", + "type": "boolean" + }, + "readRowkeyAsString": { + "description": "Optional. If field is true, then the rowkey column families will be read and converted to string. Otherwise they are read with BYTES type values and users need to manually cast them with CAST if necessary. The default value is false.", + "type": "boolean" + } + }, + "type": "object" + }, + "BigtableProtoConfig": { + "description": "Information related to a Bigtable protobuf column.", + "properties": { + "protoMessageName": { + "description": "Optional. The fully qualified proto message name of the protobuf. In the format of \"foo.bar.Message\".", + "type": "string" + }, + "schemaBundleId": { + "description": "Optional. The ID of the Bigtable SchemaBundle resource associated with this protobuf. The ID should be referred to within the parent table, e.g., `foo` rather than `projects/{project}/instances/{instance}/tables/{table}/schemaBundles/foo`. See [more details on Bigtable SchemaBundles](https://docs.cloud.google.com/bigtable/docs/create-manage-protobuf-schemas).", + "type": "string" + } + }, + "type": "object" + }, + "CloneDefinition": { + "description": "Information about base table and clone time of a table clone.", + "properties": { + "baseTableReference": { + "$ref": "#/$defs/TableReference", + "description": "Required. Reference describing the ID of the table that was cloned." + }, + "cloneTime": { + "description": "Required. The time at which the base table was cloned. This value is reported in the JSON response using RFC3339 format.", + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "Clustering": { + "description": "Configures table clustering.", + "properties": { + "fields": { + "description": "One or more fields on which data should be clustered. Only top-level, non-repeated, simple-type fields are supported. The ordering of the clustering fields should be prioritized from most to least important for filtering purposes. For additional information, see [Introduction to clustered tables](https://cloud.google.com/bigquery/docs/clustered-tables#limitations).", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "ColumnReference": { + "description": "The pair of the foreign key column and primary key column.", + "properties": { + "referencedColumn": { + "description": "Required. The column in the primary key that are referenced by the referencing_column.", + "type": "string" + }, + "referencingColumn": { + "description": "Required. The column that composes the foreign key.", + "type": "string" + } + }, + "type": "object" + }, + "CsvOptions": { + "description": "Information related to a CSV data source.", + "properties": { + "allowJaggedRows": { + "description": "Optional. Indicates if BigQuery should accept rows that are missing trailing optional columns. If true, BigQuery treats missing trailing columns as null values. If false, records with missing trailing columns are treated as bad records, and if there are too many bad records, an invalid error is returned in the job result. The default value is false.", + "type": "boolean" + }, + "allowQuotedNewlines": { + "description": "Optional. Indicates if BigQuery should allow quoted data sections that contain newline characters in a CSV file. The default value is false.", + "type": "boolean" + }, + "encoding": { + "description": "Optional. The character encoding of the data. The supported values are UTF-8, ISO-8859-1, UTF-16BE, UTF-16LE, UTF-32BE, and UTF-32LE. The default value is UTF-8. BigQuery decodes the data after the raw, binary data has been split using the values of the quote and fieldDelimiter properties.", + "type": "string" + }, + "fieldDelimiter": { + "description": "Optional. The separator character for fields in a CSV file. The separator is interpreted as a single byte. For files encoded in ISO-8859-1, any single character can be used as a separator. For files encoded in UTF-8, characters represented in decimal range 1-127 (U+0001-U+007F) can be used without any modification. UTF-8 characters encoded with multiple bytes (i.e. U+0080 and above) will have only the first byte used for separating fields. The remaining bytes will be treated as a part of the field. BigQuery also supports the escape sequence \"\\t\" (U+0009) to specify a tab separator. The default value is comma (\",\", U+002C).", + "type": "string" + }, + "nullMarker": { + "description": "Optional. Specifies a string that represents a null value in a CSV file. For example, if you specify \"\\N\", BigQuery interprets \"\\N\" as a null value when querying a CSV file. The default value is the empty string. If you set this property to a custom value, BigQuery throws an error if an empty string is present for all data types except for STRING and BYTE. For STRING and BYTE columns, BigQuery interprets the empty string as an empty value.", + "type": "string" + }, + "nullMarkers": { + "description": "Optional. A list of strings represented as SQL NULL value in a CSV file. null_marker and null_markers can't be set at the same time. If null_marker is set, null_markers has to be not set. If null_markers is set, null_marker has to be not set. If both null_marker and null_markers are set at the same time, a user error would be thrown. Any strings listed in null_markers, including empty string would be interpreted as SQL NULL. This applies to all column types.", + "items": { + "type": "string" + }, + "type": "array" + }, + "preserveAsciiControlCharacters": { + "description": "Optional. Indicates if the embedded ASCII control characters (the first 32 characters in the ASCII-table, from '\\x00' to '\\x1F') are preserved.", + "type": "boolean" + }, + "quote": { + "default": "\"", + "description": "Optional. The value that is used to quote data sections in a CSV file. BigQuery converts the string to ISO-8859-1 encoding, and then uses the first byte of the encoded string to split the data in its raw, binary state. The default value is a double-quote (\"). If your data does not contain quoted sections, set the property value to an empty string. If your data contains quoted newline characters, you must also set the allowQuotedNewlines property to true. To include the specific quote character within a quoted value, precede it with an additional matching quote character. For example, if you want to escape the default character ' \" ', use ' \"\" '.", + "pattern": ".?", + "type": "string" + }, + "skipLeadingRows": { + "description": "Optional. The number of rows at the top of a CSV file that BigQuery will skip when reading the data. The default value is 0. This property is useful if you have header rows in the file that should be skipped. When autodetect is on, the behavior is the following: * skipLeadingRows unspecified - Autodetect tries to detect headers in the first row. If they are not detected, the row is read as data. Otherwise data is read starting from the second row. * skipLeadingRows is 0 - Instructs autodetect that there are no headers and data should be read starting from the first row. * skipLeadingRows = N > 0 - Autodetect skips N-1 rows and tries to detect headers in row N. If headers are not detected, row N is just skipped. Otherwise row N is used to extract column names for the detected schema.", + "format": "int64", + "type": "string" + }, + "sourceColumnMatch": { + "description": "Optional. Controls the strategy used to match loaded columns to the schema. If not set, a sensible default is chosen based on how the schema is provided. If autodetect is used, then columns are matched by name. Otherwise, columns are matched by position. This is done to keep the behavior backward-compatible. Acceptable values are: POSITION - matches by position. This assumes that the columns are ordered the same way as the schema. NAME - matches by name. This reads the header row as column names and reorders columns to match the field names in the schema.", + "type": "string" + } + }, + "type": "object" + }, + "DataPolicyOption": { + "description": "Data policy option. For more information, see [Mask data by applying data policies to a column](https://docs.cloud.google.com/bigquery/docs/column-data-masking#data-policies-on-column).", + "properties": { + "name": { + "description": "Data policy resource name in the form of projects/project_id/locations/location_id/dataPolicies/data_policy_id.", + "type": "string" + } + }, + "type": "object" + }, + "DifferentialPrivacyPolicy": { + "description": "Represents privacy policy associated with \"differential privacy\" method.", + "properties": { + "deltaBudget": { + "description": "Optional. The total delta budget for all queries against the privacy-protected view. Each subscriber query against this view charges the amount of delta that is pre-defined by the contributor through the privacy policy delta_per_query field. If there is sufficient budget, then the subscriber query attempts to complete. It might still fail due to other reasons, in which case the charge is refunded. If there is insufficient budget the query is rejected. There might be multiple charge attempts if a single query references multiple views. In this case there must be sufficient budget for all charges or the query is rejected and charges are refunded in best effort. The budget does not have a refresh policy and can only be updated via ALTER VIEW or circumvented by creating a new view that can be queried with a fresh budget.", + "format": "double", + "type": "number" + }, + "deltaBudgetRemaining": { + "description": "Output only. The delta budget remaining. If budget is exhausted, no more queries are allowed. Note that the budget for queries that are in progress is deducted before the query executes. If the query fails or is cancelled then the budget is refunded. In this case the amount of budget remaining can increase.", + "format": "double", + "readOnly": true, + "type": "number" + }, + "deltaPerQuery": { + "description": "Optional. The delta value that is used per query. Delta represents the probability that any row will fail to be epsilon differentially private. Indicates the risk associated with exposing aggregate rows in the result of a query.", + "format": "double", + "type": "number" + }, + "epsilonBudget": { + "description": "Optional. The total epsilon budget for all queries against the privacy-protected view. Each subscriber query against this view charges the amount of epsilon they request in their query. If there is sufficient budget, then the subscriber query attempts to complete. It might still fail due to other reasons, in which case the charge is refunded. If there is insufficient budget the query is rejected. There might be multiple charge attempts if a single query references multiple views. In this case there must be sufficient budget for all charges or the query is rejected and charges are refunded in best effort. The budget does not have a refresh policy and can only be updated via ALTER VIEW or circumvented by creating a new view that can be queried with a fresh budget.", + "format": "double", + "type": "number" + }, + "epsilonBudgetRemaining": { + "description": "Output only. The epsilon budget remaining. If budget is exhausted, no more queries are allowed. Note that the budget for queries that are in progress is deducted before the query executes. If the query fails or is cancelled then the budget is refunded. In this case the amount of budget remaining can increase.", + "format": "double", + "readOnly": true, + "type": "number" + }, + "maxEpsilonPerQuery": { + "description": "Optional. The maximum epsilon value that a query can consume. If the subscriber specifies epsilon as a parameter in a SELECT query, it must be less than or equal to this value. The epsilon parameter controls the amount of noise that is added to the groups — a higher epsilon means less noise.", + "format": "double", + "type": "number" + }, + "maxGroupsContributed": { + "description": "Optional. The maximum groups contributed value that is used per query. Represents the maximum number of groups to which each protected entity can contribute. Changing this value does not improve or worsen privacy. The best value for accuracy and utility depends on the query and data.", + "format": "int64", + "type": "string" + }, + "privacyUnitColumn": { + "description": "Optional. The privacy unit column associated with this policy. Differential privacy policies can only have one privacy unit column per data source object (table, view).", + "type": "string" + } + }, + "type": "object" + }, + "EncryptionConfiguration": { + "description": "Configuration for Cloud KMS encryption settings.", + "properties": { + "kmsKeyName": { + "description": "Optional. Describes the Cloud KMS encryption key that will be used to protect destination BigQuery table. The BigQuery Service Account associated with your project requires access to this encryption key.", + "type": "string" + } + }, + "type": "object" + }, + "ErrorProto": { + "description": "Error details.", + "properties": { + "debugInfo": { + "description": "Debugging information. This property is internal to Google and should not be used.", + "type": "string" + }, + "location": { + "description": "Specifies where the error occurred, if present.", + "type": "string" + }, + "message": { + "description": "A human-readable description of the error.", + "type": "string" + }, + "reason": { + "description": "A short error code that summarizes the error.", + "type": "string" + } + }, + "type": "object" + }, + "ExternalCatalogTableOptions": { + "description": "Metadata about open source compatible table. The fields contained in these options correspond to Hive metastore's table-level properties.", + "properties": { + "connectionId": { + "description": "Optional. A connection ID that specifies the credentials to be used to read external storage, such as Azure Blob, Cloud Storage, or Amazon S3. This connection is needed to read the open source table from BigQuery. The connection_id format must be either `..` or `projects//locations//connections/`.", + "type": "string" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional. A map of the key-value pairs defining the parameters and properties of the open source table. Corresponds with Hive metastore table parameters. Maximum size of 4MiB.", + "type": "object" + }, + "storageDescriptor": { + "$ref": "#/$defs/StorageDescriptor", + "description": "Optional. A storage descriptor containing information about the physical storage of this table." + } + }, + "type": "object" + }, + "ExternalDataConfiguration": { + "properties": { + "autodetect": { + "description": "Try to detect schema and format options automatically. Any option specified explicitly will be honored.", + "type": "boolean" + }, + "avroOptions": { + "$ref": "#/$defs/AvroOptions", + "description": "Optional. Additional properties to set if sourceFormat is set to AVRO." + }, + "bigtableOptions": { + "$ref": "#/$defs/BigtableOptions", + "description": "Optional. Additional options if sourceFormat is set to BIGTABLE." + }, + "compression": { + "description": "Optional. The compression type of the data source. Possible values include GZIP and NONE. The default value is NONE. This setting is ignored for Google Cloud Bigtable, Google Cloud Datastore backups, Avro, ORC and Parquet formats. An empty string is an invalid value.", + "type": "string" + }, + "connectionId": { + "description": "Optional. The connection specifying the credentials to be used to read external storage, such as Azure Blob, Cloud Storage, or S3. The connection_id can have the form `{project_id}.{location_id};{connection_id}` or `projects/{project_id}/locations/{location_id}/connections/{connection_id}`.", + "type": "string" + }, + "csvOptions": { + "$ref": "#/$defs/CsvOptions", + "description": "Optional. Additional properties to set if sourceFormat is set to CSV." + }, + "dateFormat": { + "description": "Optional. Format used to parse DATE values. Supports C-style and SQL-style values.", + "type": "string" + }, + "datetimeFormat": { + "description": "Optional. Format used to parse DATETIME values. Supports C-style and SQL-style values.", + "type": "string" + }, + "decimalTargetTypes": { + "description": "Defines the list of possible SQL data types to which the source decimal values are converted. This list and the precision and the scale parameters of the decimal field determine the target type. In the order of NUMERIC, BIGNUMERIC, and STRING, a type is picked if it is in the specified list and if it supports the precision and the scale. STRING supports all precision and scale values. If none of the listed types supports the precision and the scale, the type supporting the widest range in the specified list is picked, and if a value exceeds the supported range when reading the data, an error will be thrown. Example: Suppose the value of this field is [\"NUMERIC\", \"BIGNUMERIC\"]. If (precision,scale) is: * (38,9) -> NUMERIC; * (39,9) -> BIGNUMERIC (NUMERIC cannot hold 30 integer digits); * (38,10) -> BIGNUMERIC (NUMERIC cannot hold 10 fractional digits); * (76,38) -> BIGNUMERIC; * (77,38) -> BIGNUMERIC (error if value exceeds supported range). This field cannot contain duplicate types. The order of the types in this field is ignored. For example, [\"BIGNUMERIC\", \"NUMERIC\"] is the same as [\"NUMERIC\", \"BIGNUMERIC\"] and NUMERIC always takes precedence over BIGNUMERIC. Defaults to [\"NUMERIC\", \"STRING\"] for ORC and [\"NUMERIC\"] for the other file formats.", + "items": { + "enum": [ + "DECIMAL_TARGET_TYPE_UNSPECIFIED", + "NUMERIC", + "BIGNUMERIC", + "STRING" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Invalid type.", + "Decimal values could be converted to NUMERIC type.", + "Decimal values could be converted to BIGNUMERIC type.", + "Decimal values could be converted to STRING type." + ] + }, + "type": "array" + }, + "fileSetSpecType": { + "description": "Optional. Specifies how source URIs are interpreted for constructing the file set to load. By default source URIs are expanded against the underlying storage. Other options include specifying manifest files. Only applicable to object storage systems.", + "enum": [ + "FILE_SET_SPEC_TYPE_FILE_SYSTEM_MATCH", + "FILE_SET_SPEC_TYPE_NEW_LINE_DELIMITED_MANIFEST" + ], + "type": "string", + "x-google-enum-descriptions": [ + "This option expands source URIs by listing files from the object store. It is the default behavior if FileSetSpecType is not set.", + "This option indicates that the provided URIs are newline-delimited manifest files, with one URI per line. Wildcard URIs are not supported." + ] + }, + "googleSheetsOptions": { + "$ref": "#/$defs/GoogleSheetsOptions", + "description": "Optional. Additional options if sourceFormat is set to GOOGLE_SHEETS." + }, + "hivePartitioningOptions": { + "$ref": "#/$defs/HivePartitioningOptions", + "description": "Optional. When set, configures hive partitioning support. Not all storage formats support hive partitioning -- requesting hive partitioning on an unsupported format will lead to an error, as will providing an invalid specification." + }, + "ignoreUnknownValues": { + "description": "Optional. Indicates if BigQuery should allow extra values that are not represented in the table schema. If true, the extra values are ignored. If false, records with extra columns are treated as bad records, and if there are too many bad records, an invalid error is returned in the job result. The default value is false. The sourceFormat property determines what BigQuery treats as an extra value: CSV: Trailing columns JSON: Named values that don't match any column names Google Cloud Bigtable: This setting is ignored. Google Cloud Datastore backups: This setting is ignored. Avro: This setting is ignored. ORC: This setting is ignored. Parquet: This setting is ignored.", + "type": "boolean" + }, + "jsonExtension": { + "description": "Optional. Load option to be used together with source_format newline-delimited JSON to indicate that a variant of JSON is being loaded. To load newline-delimited GeoJSON, specify GEOJSON (and source_format must be set to NEWLINE_DELIMITED_JSON).", + "enum": [ + "JSON_EXTENSION_UNSPECIFIED", + "GEOJSON" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The default if provided value is not one included in the enum, or the value is not specified. The source format is parsed without any modification.", + "Use GeoJSON variant of JSON. See https://tools.ietf.org/html/rfc7946." + ] + }, + "jsonOptions": { + "$ref": "#/$defs/JsonOptions", + "description": "Optional. Additional properties to set if sourceFormat is set to JSON." + }, + "maxBadRecords": { + "description": "Optional. The maximum number of bad records that BigQuery can ignore when reading data. If the number of bad records exceeds this value, an invalid error is returned in the job result. The default value is 0, which requires that all records are valid. This setting is ignored for Google Cloud Bigtable, Google Cloud Datastore backups, Avro, ORC and Parquet formats.", + "format": "int32", + "type": "integer" + }, + "metadataCacheMode": { + "description": "Optional. Metadata Cache Mode for the table. Set this to enable caching of metadata from external data source.", + "enum": [ + "METADATA_CACHE_MODE_UNSPECIFIED", + "AUTOMATIC", + "MANUAL" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified metadata cache mode.", + "Set this mode to trigger automatic background refresh of metadata cache from the external source. Queries will use the latest available cache version within the table's maxStaleness interval.", + "Set this mode to enable triggering manual refresh of the metadata cache from external source. Queries will use the latest manually triggered cache version within the table's maxStaleness interval." + ] + }, + "objectMetadata": { + "description": "Optional. ObjectMetadata is used to create Object Tables. Object Tables contain a listing of objects (with their metadata) found at the source_uris. If ObjectMetadata is set, source_format should be omitted. Currently SIMPLE is the only supported Object Metadata type.", + "enum": [ + "OBJECT_METADATA_UNSPECIFIED", + "DIRECTORY", + "SIMPLE" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified by default.", + "A synonym for `SIMPLE`.", + "Directory listing of objects." + ] + }, + "parquetOptions": { + "$ref": "#/$defs/ParquetOptions", + "description": "Optional. Additional properties to set if sourceFormat is set to PARQUET." + }, + "referenceFileSchemaUri": { + "description": "Optional. When creating an external table, the user can provide a reference file with the table schema. This is enabled for the following formats: AVRO, PARQUET, ORC.", + "type": "string" + }, + "schema": { + "$ref": "#/$defs/TableSchema", + "description": "Optional. The schema for the data. Schema is required for CSV and JSON formats if autodetect is not on. Schema is disallowed for Google Cloud Bigtable, Cloud Datastore backups, Avro, ORC and Parquet formats." + }, + "sourceFormat": { + "description": "[Required] The data format. For CSV files, specify \"CSV\". For Google sheets, specify \"GOOGLE_SHEETS\". For newline-delimited JSON, specify \"NEWLINE_DELIMITED_JSON\". For Avro files, specify \"AVRO\". For Google Cloud Datastore backups, specify \"DATASTORE_BACKUP\". For Apache Iceberg tables, specify \"ICEBERG\". For ORC files, specify \"ORC\". For Parquet files, specify \"PARQUET\". [Beta] For Google Cloud Bigtable, specify \"BIGTABLE\".", + "type": "string" + }, + "sourceUris": { + "description": "[Required] The fully-qualified URIs that point to your data in Google Cloud. For Google Cloud Storage URIs: Each URI can contain one '*' wildcard character and it must come after the 'bucket' name. Size limits related to load jobs apply to external data sources. For Google Cloud Bigtable URIs: Exactly one URI can be specified and it has be a fully specified and valid HTTPS URL for a Google Cloud Bigtable table. For Google Cloud Datastore backups, exactly one URI can be specified. Also, the '*' wildcard character is not allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "timeFormat": { + "description": "Optional. Format used to parse TIME values. Supports C-style and SQL-style values.", + "type": "string" + }, + "timeZone": { + "description": "Optional. Time zone used when parsing timestamp values that do not have specific time zone information (e.g. 2024-04-20 12:34:56). The expected format is a IANA timezone string (e.g. America/Los_Angeles).", + "type": "string" + }, + "timestampFormat": { + "description": "Optional. Format used to parse TIMESTAMP values. Supports C-style and SQL-style values.", + "type": "string" + }, + "timestampTargetPrecision": { + "description": "Precisions (maximum number of total digits in base 10) for seconds of TIMESTAMP types that are allowed to the destination table for autodetection mode. Available for the formats: CSV, PARQUET, and AVRO. Possible values include: Not Specified, [], or [6]: timestamp(6) for all auto detected TIMESTAMP columns [6, 12]: timestamp(6) for all auto detected TIMESTAMP columns that have less than 6 digits of subseconds. timestamp(12) for all auto detected TIMESTAMP columns that have more than 6 digits of subseconds. [12]: timestamp(12) for all auto detected TIMESTAMP columns. The order of the elements in this array is ignored. Inputs that have higher precision than the highest target precision in this array will be truncated.", + "items": { + "format": "int32", + "type": "integer" + }, + "type": "array" + } + }, + "type": "object" + }, + "FieldElementType": { + "description": "Represents the type of a field element.", + "properties": { + "type": { + "description": "Required. The type of a field element. For more information, see TableFieldSchema.type.", + "type": "string" + } + }, + "type": "object" + }, + "ForeignKey": { + "description": "Represents a foreign key constraint on a table's columns.", + "properties": { + "columnReferences": { + "description": "Required. The columns that compose the foreign key.", + "items": { + "$ref": "#/$defs/ColumnReference" + }, + "type": "array" + }, + "name": { + "description": "Optional. Set only if the foreign key constraint is named.", + "type": "string" + }, + "referencedTable": { + "$ref": "#/$defs/TableReference", + "description": "Required. The table that holds the primary key and is referenced by this foreign key." + } + }, + "type": "object" + }, + "ForeignTypeInfo": { + "description": "Metadata about the foreign data type definition such as the system in which the type is defined.", + "properties": { + "typeSystem": { + "description": "Required. Specifies the system which defines the foreign data type.", + "enum": [ + "TYPE_SYSTEM_UNSPECIFIED", + "HIVE" + ], + "type": "string", + "x-google-enum-descriptions": [ + "TypeSystem not specified.", + "Represents Hive data types." + ] + } + }, + "type": "object" + }, + "ForeignViewDefinition": { + "description": "A view can be represented in multiple ways. Each representation has its own dialect. This message stores the metadata required for these representations.", + "properties": { + "dialect": { + "description": "Optional. Represents the dialect of the query.", + "type": "string" + }, + "query": { + "description": "Required. The query that defines the view.", + "type": "string" + } + }, + "type": "object" + }, + "GeneratedColumn": { + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields).", + "properties": { + "generatedExpressionInfo": { + "$ref": "#/$defs/GeneratedExpressionInfo", + "description": "Definition of the expression used to generate the field." + }, + "generatedMode": { + "description": "Optional. Dictates when system generated values are used to populate the field.", + "enum": [ + "GENERATED_MODE_UNSPECIFIED", + "GENERATED_ALWAYS", + "GENERATED_BY_DEFAULT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified GeneratedMode will default to GENERATED_ALWAYS.", + "Field can only have system generated values. Users cannot manually insert values into the field.", + "Use system generated values only if the user does not explicitly provide a value." + ] + } + }, + "type": "object" + }, + "GeneratedExpressionInfo": { + "description": "Definition of the expression used to generate the field.", + "properties": { + "asynchronous": { + "description": "Optional. Whether the column generation is done asynchronously.", + "type": "boolean" + }, + "generationExpression": { + "description": "Optional. The generation expression (e.g. AI.EMBED(...)) used to generated the field.", + "type": "string" + }, + "stored": { + "description": "Optional. Whether the generated column is stored in the table.", + "type": "boolean" + } + }, + "type": "object" + }, + "GoogleSheetsOptions": { + "description": "Options specific to Google Sheets data sources.", + "properties": { + "range": { + "description": "Optional. Range of a sheet to query from. Only used when non-empty. Typical format: sheet_name!top_left_cell_id:bottom_right_cell_id For example: sheet1!A1:B20", + "type": "string" + }, + "skipLeadingRows": { + "description": "Optional. The number of rows at the top of a sheet that BigQuery will skip when reading the data. The default value is 0. This property is useful if you have header rows that should be skipped. When autodetect is on, the behavior is the following: * skipLeadingRows unspecified - Autodetect tries to detect headers in the first row. If they are not detected, the row is read as data. Otherwise data is read starting from the second row. * skipLeadingRows is 0 - Instructs autodetect that there are no headers and data should be read starting from the first row. * skipLeadingRows = N > 0 - Autodetect skips N-1 rows and tries to detect headers in row N. If headers are not detected, row N is just skipped. Otherwise row N is used to extract column names for the detected schema.", + "format": "int64", + "type": "string" + } + }, + "type": "object" + }, + "HivePartitioningOptions": { + "description": "Options for configuring hive partitioning detect.", + "properties": { + "fields": { + "description": "Output only. For permanent external tables, this field is populated with the hive partition keys in the order they were inferred. The types of the partition keys can be deduced by checking the table schema (which will include the partition keys). Not every API will populate this field in the output. For example, Tables.Get will populate it, but Tables.List will not contain this field.", + "items": { + "type": "string" + }, + "readOnly": true, + "type": "array" + }, + "mode": { + "description": "Optional. When set, what mode of hive partitioning to use when reading data. The following modes are supported: * AUTO: automatically infer partition key name(s) and type(s). * STRINGS: automatically infer partition key name(s). All types are strings. * CUSTOM: partition key schema is encoded in the source URI prefix. Not all storage formats support hive partitioning. Requesting hive partitioning on an unsupported format will lead to an error. Currently supported formats are: JSON, CSV, ORC, Avro and Parquet.", + "type": "string" + }, + "requirePartitionFilter": { + "default": "false", + "description": "Optional. If set to true, queries over this table require a partition filter that can be used for partition elimination to be specified. Note that this field should only be true when creating a permanent external table or querying a temporary external table. Hive-partitioned loads with require_partition_filter explicitly set to true will fail.", + "type": "boolean" + }, + "sourceUriPrefix": { + "description": "Optional. When hive partition detection is requested, a common prefix for all source uris must be required. The prefix must end immediately before the partition key encoding begins. For example, consider files following this data layout: gs://bucket/path_to_table/dt=2019-06-01/country=USA/id=7/file.avro gs://bucket/path_to_table/dt=2019-05-31/country=CA/id=3/file.avro When hive partitioning is requested with either AUTO or STRINGS detection, the common prefix can be either of gs://bucket/path_to_table or gs://bucket/path_to_table/. CUSTOM detection requires encoding the partitioning schema immediately after the common prefix. For CUSTOM, any of * gs://bucket/path_to_table/{dt:DATE}/{country:STRING}/{id:INTEGER} * gs://bucket/path_to_table/{dt:STRING}/{country:STRING}/{id:INTEGER} * gs://bucket/path_to_table/{dt:DATE}/{country:STRING}/{id:STRING} would all be valid source URI prefixes.", + "type": "string" + } + }, + "type": "object" + }, + "JoinRestrictionPolicy": { + "description": "Represents privacy policy associated with \"join restrictions\". Join restriction gives data providers the ability to enforce joins on the 'join_allowed_columns' when data is queried from a privacy protected view.", + "properties": { + "joinAllowedColumns": { + "description": "Optional. The only columns that joins are allowed on. This field is must be specified for join_conditions JOIN_ANY and JOIN_ALL and it cannot be set for JOIN_BLOCKED.", + "items": { + "type": "string" + }, + "type": "array" + }, + "joinCondition": { + "description": "Optional. Specifies if a join is required or not on queries for the view. Default is JOIN_CONDITION_UNSPECIFIED.", + "enum": [ + "JOIN_CONDITION_UNSPECIFIED", + "JOIN_ANY", + "JOIN_ALL", + "JOIN_NOT_REQUIRED", + "JOIN_BLOCKED" + ], + "type": "string", + "x-google-enum-descriptions": [ + "A join is neither required nor restricted on any column. Default value.", + "A join is required on at least one of the specified columns.", + "A join is required on all specified columns.", + "A join is not required, but if present it is only permitted on 'join_allowed_columns'", + "Joins are blocked for all queries." + ] + } + }, + "type": "object" + }, + "JsonOptions": { + "description": "Json Options for load and make external tables.", + "properties": { + "encoding": { + "description": "Optional. The character encoding of the data. The supported values are UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and UTF-32LE. The default value is UTF-8.", + "type": "string" + } + }, + "type": "object" + }, + "MaterializedViewDefinition": { + "description": "Definition and configuration of a materialized view.", + "properties": { + "allowNonIncrementalDefinition": { + "description": "Optional. This option declares the intention to construct a materialized view that isn't refreshed incrementally. Non-incremental materialized views support an expanded range of SQL queries. The `allow_non_incremental_definition` option can't be changed after the materialized view is created.", + "type": "boolean" + }, + "enableRefresh": { + "description": "Optional. Enable automatic refresh of the materialized view when the base table is updated. The default value is \"true\".", + "type": "boolean" + }, + "lastRefreshTime": { + "description": "Output only. The time when this materialized view was last refreshed, in milliseconds since the epoch.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "query": { + "description": "Required. A query whose results are persisted.", + "type": "string" + }, + "refreshIntervalMs": { + "description": "Optional. The maximum frequency at which this materialized view will be refreshed. The default value is \"1800000\" (30 minutes).", + "format": "uint64", + "type": "string" + } + }, + "type": "object" + }, + "MaterializedViewStatus": { + "description": "Status of a materialized view. The last refresh timestamp status is omitted here, but is present in the MaterializedViewDefinition message.", + "properties": { + "lastRefreshStatus": { + "$ref": "#/$defs/ErrorProto", + "description": "Output only. Error result of the last automatic refresh. If present, indicates that the last automatic refresh was unsuccessful.", + "readOnly": true + }, + "refreshWatermark": { + "description": "Output only. Refresh watermark of materialized view. The base tables' data were collected into the materialized view cache until this time.", + "format": "date-time", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + }, + "ParquetOptions": { + "description": "Parquet Options for load and make external tables.", + "properties": { + "enableListInference": { + "description": "Optional. Indicates whether to use schema inference specifically for Parquet LIST logical type.", + "type": "boolean" + }, + "enumAsString": { + "description": "Optional. Indicates whether to infer Parquet ENUM logical type as STRING instead of BYTES by default.", + "type": "boolean" + }, + "mapTargetType": { + "description": "Optional. Indicates how to represent a Parquet map if present.", + "enum": [ + "MAP_TARGET_TYPE_UNSPECIFIED", + "ARRAY_OF_STRUCT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "In this mode, the map will have the following schema: struct map_field_name { repeated struct key_value { key value } }.", + "In this mode, the map will have the following schema: repeated struct map_field_name { key value }." + ] + } + }, + "type": "object" + }, + "PartitionedColumn": { + "description": "The partitioning column information.", + "properties": { + "field": { + "description": "Required. The name of the partition column.", + "type": "string" + } + }, + "type": "object" + }, + "PartitioningDefinition": { + "description": "The partitioning information, which includes managed table, external table and metastore partitioned table partition information.", + "properties": { + "partitionedColumn": { + "description": "Optional. Details about each partitioning column. This field is output only for all partitioning types other than metastore partitioned tables. BigQuery native tables only support 1 partitioning column. Other table types may support 0, 1 or more partitioning columns. For metastore partitioned tables, the order must match the definition order in the Hive Metastore, where it must match the physical layout of the table. For example, CREATE TABLE a_table(id BIGINT, name STRING) PARTITIONED BY (city STRING, state STRING). In this case the values must be ['city', 'state'] in that order.", + "items": { + "$ref": "#/$defs/PartitionedColumn" + }, + "type": "array" + } + }, + "type": "object" + }, + "PolicyTagList": { + "properties": { + "names": { + "description": "A list of policy tag resource names. For example, \"projects/1/locations/eu/taxonomies/2/policyTags/3\". At most 1 policy tag is currently allowed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "PrimaryKey": { + "description": "Represents the primary key constraint on a table's columns.", + "properties": { + "columns": { + "description": "Required. The columns that are composed of the primary key constraint.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "PrivacyPolicy": { + "description": "Represents privacy policy that contains the privacy requirements specified by the data owner. Currently, this is only supported on views.", + "properties": { + "aggregationThresholdPolicy": { + "$ref": "#/$defs/AggregationThresholdPolicy", + "description": "Optional. Policy used for aggregation thresholds." + }, + "differentialPrivacyPolicy": { + "$ref": "#/$defs/DifferentialPrivacyPolicy", + "description": "Optional. Policy used for differential privacy." + }, + "joinRestrictionPolicy": { + "$ref": "#/$defs/JoinRestrictionPolicy", + "description": "Optional. Join restriction policy is outside of the one of policies, since this policy can be set along with other policies. This policy gives data providers the ability to enforce joins on the 'join_allowed_columns' when data is queried from a privacy protected view." + } + }, + "type": "object" + }, + "Range": { + "description": "Defines the ranges for range partitioning.", + "properties": { + "end": { + "description": "Required. The end of range partitioning, exclusive. This field is an INT64 value represented as a string.", + "type": "string" + }, + "interval": { + "description": "Required. The width of each interval. This field is an INT64 value represented as a string.", + "type": "string" + }, + "start": { + "description": "Required. The start of range partitioning, inclusive. This field is an INT64 value represented as a string.", + "type": "string" + } + }, + "type": "object" + }, + "RangePartitioning": { + "properties": { + "field": { + "description": "Required. The name of the column to partition the table on. It must be a top-level, INT64 column whose mode is NULLABLE or REQUIRED.", + "type": "string" + }, + "range": { + "$ref": "#/$defs/Range", + "description": "Defines the ranges for range partitioning." + } + }, + "type": "object" + }, + "RestrictionConfig": { + "properties": { + "type": { + "description": "Output only. Specifies the type of dataset/table restriction.", + "enum": [ + "RESTRICTION_TYPE_UNSPECIFIED", + "RESTRICTED_DATA_EGRESS" + ], + "readOnly": true, + "type": "string", + "x-google-enum-descriptions": [ + "Should never be used.", + "Restrict data egress. See [Data egress](https://cloud.google.com/bigquery/docs/analytics-hub-introduction#data_egress) for more details." + ] + } + }, + "type": "object" + }, + "SerDeInfo": { + "description": "Serializer and deserializer information.", + "properties": { + "name": { + "description": "Optional. Name of the SerDe. The maximum length is 256 characters.", + "type": "string" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional. Key-value pairs that define the initialization parameters for the serialization library. Maximum size 10 Kib.", + "type": "object" + }, + "serializationLibrary": { + "description": "Required. Specifies a fully-qualified class name of the serialization library that is responsible for the translation of data between table representation and the underlying low-level input and output format structures. The maximum length is 256 characters.", + "type": "string" + } + }, + "type": "object" + }, + "SnapshotDefinition": { + "description": "Information about base table and snapshot time of the snapshot.", + "properties": { + "baseTableReference": { + "$ref": "#/$defs/TableReference", + "description": "Required. Reference describing the ID of the table that was snapshot." + }, + "snapshotTime": { + "description": "Required. The time at which the base table was snapshot. This value is reported in the JSON response using RFC3339 format.", + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "StorageDescriptor": { + "description": "Contains information about how a table's data is stored and accessed by open source query engines.", + "properties": { + "inputFormat": { + "description": "Optional. Specifies the fully qualified class name of the InputFormat (e.g. \"org.apache.hadoop.hive.ql.io.orc.OrcInputFormat\"). The maximum length is 128 characters.", + "type": "string" + }, + "locationUri": { + "description": "Optional. The physical location of the table (e.g. `gs://spark-dataproc-data/pangea-data/case_sensitive/` or `gs://spark-dataproc-data/pangea-data/*`). The maximum length is 2056 bytes.", + "type": "string" + }, + "outputFormat": { + "description": "Optional. Specifies the fully qualified class name of the OutputFormat (e.g. \"org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat\"). The maximum length is 128 characters.", + "type": "string" + }, + "serdeInfo": { + "$ref": "#/$defs/SerDeInfo", + "description": "Optional. Serializer and deserializer information." + } + }, + "type": "object" + }, + "Streamingbuffer": { + "properties": { + "estimatedBytes": { + "description": "Output only. A lower-bound estimate of the number of bytes currently in the streaming buffer.", + "format": "uint64", + "readOnly": true, + "type": "string" + }, + "estimatedRows": { + "description": "Output only. A lower-bound estimate of the number of rows currently in the streaming buffer.", + "format": "uint64", + "readOnly": true, + "type": "string" + }, + "oldestEntryTime": { + "description": "Output only. Contains the timestamp of the oldest entry in the streaming buffer, in milliseconds since the epoch, if the streaming buffer is available.", + "format": "uint64", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + }, + "TableConstraints": { + "description": "The TableConstraints defines the primary key and foreign key.", + "properties": { + "foreignKeys": { + "description": "Optional. Present only if the table has a foreign key. The foreign key is not enforced.", + "items": { + "$ref": "#/$defs/ForeignKey" + }, + "type": "array" + }, + "primaryKey": { + "$ref": "#/$defs/PrimaryKey", + "description": "Optional. Represents a primary key constraint on a table's columns. Present only if the table has a primary key. The primary key is not enforced." + } + }, + "type": "object" + }, + "TableFieldSchema": { + "description": "A field in TableSchema", + "properties": { + "collation": { + "description": "Optional. Field collation can be set only when the type of field is STRING. The following values are supported: * 'und:ci': undetermined locale, case insensitive. * '': empty string. Default to case-sensitive behavior.", + "type": "string" + }, + "dataPolicies": { + "description": "Optional. Data policies attached to this field, used for field-level access control.", + "items": { + "$ref": "#/$defs/DataPolicyOption" + }, + "type": "array" + }, + "defaultValueExpression": { + "description": "Optional. A SQL expression to specify the [default value] (https://cloud.google.com/bigquery/docs/default-values) for this field.", + "type": "string" + }, + "description": { + "description": "Optional. The field description. The maximum length is 1,024 characters.", + "type": "string" + }, + "fields": { + "description": "Optional. Describes the nested schema fields if the type property is set to RECORD.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeDefinition": { + "description": "Optional. Definition of the foreign data type. Only valid for top-level schema fields (not nested fields). If the type is FOREIGN, this field is required.", + "type": "string" + }, + "generatedColumn": { + "$ref": "#/$defs/GeneratedColumn", + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields)." + }, + "maxLength": { + "description": "Optional. Maximum length of values of this field for STRINGS or BYTES. If max_length is not specified, no maximum length constraint is imposed on this field. If type = \"STRING\", then max_length represents the maximum UTF-8 length of strings in this field. If type = \"BYTES\", then max_length represents the maximum number of bytes in this field. It is invalid to set this field if type ≠ \"STRING\" and ≠ \"BYTES\".", + "format": "int64", + "type": "string" + }, + "mode": { + "description": "Optional. The field mode. Possible values include NULLABLE, REQUIRED and REPEATED. The default value is NULLABLE.", + "type": "string" + }, + "name": { + "description": "Required. The field name. The name must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_), and must start with a letter or underscore. The maximum length is 300 characters.", + "type": "string" + }, + "policyTags": { + "$ref": "#/$defs/PolicyTagList", + "description": "Optional. The policy tags attached to this field, used for field-level access control. If not set, defaults to empty policy_tags." + }, + "precision": { + "description": "Optional. Precision (maximum number of total digits in base 10) and scale (maximum number of digits in the fractional part in base 10) constraints for values of this field for NUMERIC or BIGNUMERIC. It is invalid to set precision or scale if type ≠ \"NUMERIC\" and ≠ \"BIGNUMERIC\". If precision and scale are not specified, no value range constraint is imposed on this field insofar as values are permitted by the type. Values of this NUMERIC or BIGNUMERIC field must be in this range when: * Precision (P) and scale (S) are specified: [-10P-S + 10-S, 10P-S - 10-S] * Precision (P) is specified but not scale (and thus scale is interpreted to be equal to zero): [-10P + 1, 10P - 1]. Acceptable values for precision and scale if both are specified: * If type = \"NUMERIC\": 1 ≤ precision - scale ≤ 29 and 0 ≤ scale ≤ 9. * If type = \"BIGNUMERIC\": 1 ≤ precision - scale ≤ 38 and 0 ≤ scale ≤ 38. Acceptable values for precision if only precision is specified but not scale (and thus scale is interpreted to be equal to zero): * If type = \"NUMERIC\": 1 ≤ precision ≤ 29. * If type = \"BIGNUMERIC\": 1 ≤ precision ≤ 38. If scale is specified but not precision, then it is invalid.", + "format": "int64", + "type": "string" + }, + "rangeElementType": { + "$ref": "#/$defs/FieldElementType", + "description": "Optional. The subtype of the RANGE, if the type of this field is RANGE. If the type is RANGE, this field is required. Values for the field element type can be the following: * DATE * DATETIME * TIMESTAMP" + }, + "roundingMode": { + "description": "Optional. Specifies the rounding mode to be used when storing values of NUMERIC and BIGNUMERIC type.", + "enum": [ + "ROUNDING_MODE_UNSPECIFIED", + "ROUND_HALF_AWAY_FROM_ZERO", + "ROUND_HALF_EVEN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO.", + "ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2", + "ROUND_HALF_EVEN rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2" + ] + }, + "scale": { + "description": "Optional. See documentation for precision.", + "format": "int64", + "type": "string" + }, + "timestampPrecision": { + "default": "6", + "description": "Optional. Precision (maximum number of total digits in base 10) for seconds of TIMESTAMP type. Possible values include: * 6 (Default, for TIMESTAMP type with microsecond precision) * 12 (For TIMESTAMP type with picosecond precision)", + "format": "int64", + "type": "string" + }, + "type": { + "description": "Required. The field data type. Possible values include: * STRING * BYTES * INTEGER (or INT64) * FLOAT (or FLOAT64) * BOOLEAN (or BOOL) * TIMESTAMP * DATE * TIME * DATETIME * GEOGRAPHY * NUMERIC * BIGNUMERIC * JSON * RECORD (or STRUCT) * RANGE Use of RECORD/STRUCT indicates that the field contains a nested schema.", + "type": "string" + } + }, + "type": "object" + }, + "TableReference": { + "properties": { + "datasetId": { + "description": "Required. The ID of the dataset containing this table.", + "type": "string" + }, + "projectId": { + "description": "Required. The ID of the project containing this table.", + "type": "string" + }, + "tableId": { + "description": "Required. The ID of the table. The ID can contain Unicode characters in category L (letter), M (mark), N (number), Pc (connector, including underscore), Pd (dash), and Zs (space). For more information, see [General Category](https://wikipedia.org/wiki/Unicode_character_property#General_Category). The maximum length is 1,024 characters. Certain operations allow suffixing of the table ID with a partition decorator, such as `sample_table$20190123`.", + "type": "string" + } + }, + "type": "object" + }, + "TableReplicationInfo": { + "description": "Replication info of a table created using `AS REPLICA` DDL like: `CREATE MATERIALIZED VIEW mv1 AS REPLICA OF src_mv`", + "properties": { + "replicatedSourceLastRefreshTime": { + "description": "Optional. Output only. If source is a materialized view, this field signifies the last refresh time of the source.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "replicationError": { + "$ref": "#/$defs/ErrorProto", + "description": "Optional. Output only. Replication error that will permanently stopped table replication.", + "readOnly": true + }, + "replicationIntervalMs": { + "description": "Optional. Specifies the interval at which the source table is polled for updates. It's Optional. If not specified, default replication interval would be applied.", + "format": "int64", + "type": "string" + }, + "replicationStatus": { + "description": "Optional. Output only. Replication status of configured replication.", + "enum": [ + "REPLICATION_STATUS_UNSPECIFIED", + "ACTIVE", + "SOURCE_DELETED", + "PERMISSION_DENIED", + "UNSUPPORTED_CONFIGURATION" + ], + "readOnly": true, + "type": "string", + "x-google-enum-descriptions": [ + "Default value.", + "Replication is Active with no errors.", + "Source object is deleted.", + "Source revoked replication permissions.", + "Source configuration doesn’t allow replication." + ] + }, + "sourceTable": { + "$ref": "#/$defs/TableReference", + "description": "Required. Source table reference that is replicated." + } + }, + "type": "object" + }, + "TableSchema": { + "description": "Schema of a table", + "properties": { + "fields": { + "description": "Describes the fields in a table.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeInfo": { + "$ref": "#/$defs/ForeignTypeInfo", + "description": "Optional. Specifies metadata of the foreign data type definition in field schema (TableFieldSchema.foreign_type_definition)." + } + }, + "type": "object" + }, + "TimePartitioning": { + "properties": { + "expirationMs": { + "description": "Optional. Number of milliseconds for which to keep the storage for a partition. A wrapper is used here because 0 is an invalid value.", + "format": "int64", + "type": "string" + }, + "field": { + "description": "Optional. If not set, the table is partitioned by pseudo column '_PARTITIONTIME'; if set, the table is partitioned by this field. The field must be a top-level TIMESTAMP or DATE field. Its mode must be NULLABLE or REQUIRED. A wrapper is used here because an empty string is an invalid value.", + "type": "string" + }, + "requirePartitionFilter": { + "default": "false", + "deprecated": true, + "description": "If set to true, queries over this table require a partition filter that can be used for partition elimination to be specified. This field is deprecated; please set the field with the same name on the table itself instead. This field needs a wrapper because we want to output the default value, false, if the user explicitly set it.", + "type": "boolean" + }, + "type": { + "description": "Required. The supported types are DAY, HOUR, MONTH, and YEAR, which will generate one partition per day, hour, month, and year, respectively.", + "type": "string" + } + }, + "type": "object" + }, + "UserDefinedFunctionResource": { + "description": " This is used for defining User Defined Function (UDF) resources only when using legacy SQL. Users of GoogleSQL should leverage either DDL (e.g. CREATE [TEMPORARY] FUNCTION ... ) or the Routines API to define UDF resources. For additional information on migrating, see: https://cloud.google.com/bigquery/docs/reference/standard-sql/migrating-from-legacy-sql#differences_in_user-defined_javascript_functions", + "properties": { + "inlineCode": { + "description": "[Pick one] An inline resource that contains code for a user-defined function (UDF). Providing a inline code resource is equivalent to providing a URI for a file containing the same code.", + "type": "string" + }, + "resourceUri": { + "description": "[Pick one] A code resource to load from a Google Cloud Storage URI (gs://bucket/path).", + "type": "string" + } + }, + "type": "object" + }, + "ViewDefinition": { + "description": "Describes the definition of a logical view.", + "properties": { + "foreignDefinitions": { + "description": "Optional. Foreign view representations.", + "items": { + "$ref": "#/$defs/ForeignViewDefinition" + }, + "type": "array" + }, + "privacyPolicy": { + "$ref": "#/$defs/PrivacyPolicy", + "description": "Optional. Specifies the privacy policy for the view." + }, + "query": { + "description": "Required. A query that BigQuery executes when the view is referenced.", + "type": "string" + }, + "useExplicitColumnNames": { + "description": "True if the column names are explicitly specified. For example by using the 'CREATE VIEW v(c1, c2) AS ...' syntax. Can only be set for GoogleSQL views.", + "type": "boolean" + }, + "useLegacySql": { + "description": "Specifies whether to use BigQuery's legacy SQL for this view. The default value is true. If set to false, the view uses BigQuery's [GoogleSQL](https://docs.cloud.google.com/bigquery/docs/introduction-sql). Queries and views that reference this view must use the same flag value. A wrapper is used here because the default value is True.", + "type": "boolean" + }, + "userDefinedFunctionResources": { + "description": "Describes user-defined function resources used in the query.", + "items": { + "$ref": "#/$defs/UserDefinedFunctionResource" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "properties": { + "biglakeConfiguration": { + "$ref": "#/$defs/BigLakeConfiguration", + "description": "Optional. Specifies the configuration of a BigQuery table for Apache Iceberg." + }, + "cloneDefinition": { + "$ref": "#/$defs/CloneDefinition", + "description": "Output only. Contains information about the clone. This value is set via the clone operation.", + "readOnly": true + }, + "clustering": { + "$ref": "#/$defs/Clustering", + "description": "Clustering specification for the table. Must be specified with time-based partitioning, data in the table will be first partitioned and subsequently clustered." + }, + "creationTime": { + "description": "Output only. The time when this table was created, in milliseconds since the epoch.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "defaultCollation": { + "description": "Optional. Defines the default collation specification of new STRING fields in the table. During table creation or update, if a STRING field is added to this table without explicit collation specified, then the table inherits the table default collation. A change to this field affects only fields added afterwards, and does not alter the existing fields. The following values are supported: * 'und:ci': undetermined locale, case insensitive. * '': empty string. Default to case-sensitive behavior.", + "type": "string" + }, + "defaultRoundingMode": { + "description": "Optional. Defines the default rounding mode specification of new decimal fields (NUMERIC OR BIGNUMERIC) in the table. During table creation or update, if a decimal field is added to this table without an explicit rounding mode specified, then the field inherits the table default rounding mode. Changing this field doesn't affect existing fields.", + "enum": [ + "ROUNDING_MODE_UNSPECIFIED", + "ROUND_HALF_AWAY_FROM_ZERO", + "ROUND_HALF_EVEN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO.", + "ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2", + "ROUND_HALF_EVEN rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2" + ] + }, + "description": { + "description": "Optional. A user-friendly description of this table.", + "type": "string" + }, + "encryptionConfiguration": { + "$ref": "#/$defs/EncryptionConfiguration", + "description": "Custom encryption configuration (e.g., Cloud KMS keys)." + }, + "etag": { + "description": "Output only. A hash of this resource.", + "readOnly": true, + "type": "string" + }, + "expirationTime": { + "description": "Optional. The time when this table expires, in milliseconds since the epoch. If not present, the table will persist indefinitely. Expired tables will be deleted and their storage reclaimed. The defaultTableExpirationMs property of the encapsulating dataset can be used to set a default expirationTime on newly created tables.", + "format": "int64", + "type": "string" + }, + "externalCatalogTableOptions": { + "$ref": "#/$defs/ExternalCatalogTableOptions", + "description": "Optional. Options defining open source compatible table." + }, + "externalDataConfiguration": { + "$ref": "#/$defs/ExternalDataConfiguration", + "description": "Optional. Describes the data format, location, and other properties of a table stored outside of BigQuery. By defining these properties, the data source can then be queried as if it were a standard BigQuery table." + }, + "friendlyName": { + "description": "Optional. A descriptive name for this table.", + "type": "string" + }, + "id": { + "description": "Output only. An opaque ID uniquely identifying the table.", + "readOnly": true, + "type": "string" + }, + "kind": { + "default": "bigquery#table", + "description": "The type of resource ID.", + "type": "string" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "description": "The labels associated with this table. You can use these to organize and group your tables. Label keys and values can be no longer than 63 characters, can only contain lowercase letters, numeric characters, underscores and dashes. International characters are allowed. Label values are optional. Label keys must start with a letter and each label in the list must have a different key.", + "type": "object" + }, + "lastModifiedTime": { + "description": "Output only. The time when this table was last modified, in milliseconds since the epoch.", + "format": "uint64", + "readOnly": true, + "type": "string" + }, + "location": { + "description": "Output only. The geographic location where the table resides. This value is inherited from the dataset.", + "readOnly": true, + "type": "string" + }, + "managedTableType": { + "description": "Optional. If set, overrides the default managed table type configured in the dataset.", + "enum": [ + "MANAGED_TABLE_TYPE_UNSPECIFIED", + "NATIVE", + "BIGLAKE" + ], + "type": "string", + "x-google-enum-descriptions": [ + "No managed table type specified.", + "The managed table is a native BigQuery table.", + "The managed table is a BigLake table for Apache Iceberg in BigQuery." + ] + }, + "materializedView": { + "$ref": "#/$defs/MaterializedViewDefinition", + "description": "Optional. The materialized view definition." + }, + "materializedViewStatus": { + "$ref": "#/$defs/MaterializedViewStatus", + "description": "Output only. The materialized view status.", + "readOnly": true + }, + "maxStaleness": { + "description": "Optional. The maximum staleness of data that could be returned when the table (or stale MV) is queried. Staleness encoded as a string encoding of sql IntervalValue type.", + "type": "string" + }, + "numActiveLogicalBytes": { + "description": "Output only. Number of logical bytes that are less than 90 days old.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numActivePhysicalBytes": { + "description": "Output only. Number of physical bytes less than 90 days old. This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numBytes": { + "description": "Output only. The size of this table in logical bytes, excluding any data in the streaming buffer.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numCurrentPhysicalBytes": { + "description": "Output only. Number of physical bytes used by current live data storage. This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numLongTermBytes": { + "description": "Output only. The number of logical bytes in the table that are considered \"long-term storage\".", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numLongTermLogicalBytes": { + "description": "Output only. Number of logical bytes that are more than 90 days old.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numLongTermPhysicalBytes": { + "description": "Output only. Number of physical bytes more than 90 days old. This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numPartitions": { + "description": "Output only. The number of partitions present in the table or materialized view. This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numPhysicalBytes": { + "description": "Output only. The physical size of this table in bytes. This includes storage used for time travel.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numRows": { + "description": "Output only. The number of rows of data in this table, excluding any data in the streaming buffer.", + "format": "uint64", + "readOnly": true, + "type": "string" + }, + "numTimeTravelPhysicalBytes": { + "description": "Output only. Number of physical bytes used by time travel storage (deleted or changed data). This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numTotalLogicalBytes": { + "description": "Output only. Total number of logical bytes in the table or materialized view.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "numTotalPhysicalBytes": { + "description": "Output only. The physical size of this table in bytes. This also includes storage used for time travel. This data is not kept in real time, and might be delayed by a few seconds to a few minutes.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "partitionDefinition": { + "$ref": "#/$defs/PartitioningDefinition", + "description": "Optional. The partition information for all table formats, including managed partitioned tables, hive partitioned tables, iceberg partitioned, and metastore partitioned tables. This field is only populated for metastore partitioned tables. For other table formats, this is an output only field." + }, + "rangePartitioning": { + "$ref": "#/$defs/RangePartitioning", + "description": "If specified, configures range partitioning for this table." + }, + "replicas": { + "description": "Optional. Output only. Table references of all replicas currently active on the table.", + "items": { + "$ref": "#/$defs/TableReference" + }, + "readOnly": true, + "type": "array" + }, + "requirePartitionFilter": { + "default": "false", + "description": "Optional. If set to true, queries over this table require a partition filter that can be used for partition elimination to be specified.", + "type": "boolean" + }, + "resourceTags": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional. The [tags](https://cloud.google.com/bigquery/docs/tags) attached to this table. Tag keys are globally unique. Tag key is expected to be in the namespaced format, for example \"123456789012/environment\" where 123456789012 is the ID of the parent organization or project resource for this tag key. Tag value is expected to be the short name, for example \"Production\". See [Tag definitions](https://cloud.google.com/iam/docs/tags-access-control#definitions) for more details.", + "type": "object" + }, + "restrictions": { + "$ref": "#/$defs/RestrictionConfig", + "description": "Optional. Output only. Restriction config for table. If set, restrict certain accesses on the table based on the config. See [Data egress](https://cloud.google.com/bigquery/docs/analytics-hub-introduction#data_egress) for more details.", + "readOnly": true + }, + "schema": { + "$ref": "#/$defs/TableSchema", + "description": "Optional. Describes the schema of this table." + }, + "selfLink": { + "description": "Output only. A URL that can be used to access this resource again.", + "readOnly": true, + "type": "string" + }, + "snapshotDefinition": { + "$ref": "#/$defs/SnapshotDefinition", + "description": "Output only. Contains information about the snapshot. This value is set via snapshot creation.", + "readOnly": true + }, + "streamingBuffer": { + "$ref": "#/$defs/Streamingbuffer", + "description": "Output only. Contains information regarding this table's streaming buffer, if one is present. This field will be absent if the table is not being streamed to or if there is no data in the streaming buffer.", + "readOnly": true + }, + "tableConstraints": { + "$ref": "#/$defs/TableConstraints", + "description": "Optional. Tables Primary Key and Foreign Key information" + }, + "tableReference": { + "$ref": "#/$defs/TableReference", + "description": "Required. Reference describing the ID of this table." + }, + "tableReplicationInfo": { + "$ref": "#/$defs/TableReplicationInfo", + "description": "Optional. Table replication info for table created `AS REPLICA` DDL like: `CREATE MATERIALIZED VIEW mv1 AS REPLICA OF src_mv`" + }, + "timePartitioning": { + "$ref": "#/$defs/TimePartitioning", + "description": "If specified, configures time-based partitioning for this table." + }, + "type": { + "description": "Output only. Describes the table type. The following values are supported: * `TABLE`: A normal BigQuery table. * `VIEW`: A virtual table defined by a SQL query. * `EXTERNAL`: A table that references data stored in an external storage system, such as Google Cloud Storage. * `MATERIALIZED_VIEW`: A precomputed view defined by a SQL query. * `SNAPSHOT`: An immutable BigQuery table that preserves the contents of a base table at a particular time. See additional information on [table snapshots](https://cloud.google.com/bigquery/docs/table-snapshots-intro). The default value is `TABLE`.", + "readOnly": true, + "type": "string" + }, + "view": { + "$ref": "#/$defs/ViewDefinition", + "description": "Optional. The view definition." + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Run a read-only SQL query in the project and return the result. Prefer this tool over\n`execute_sql` if possible.\n\nThis tool is restricted to only `SELECT` statements. `INSERT`, `UPDATE`, and `DELETE`\nstatements and stored procedures aren't allowed. If the query doesn't include a `SELECT`\nstatement, an error is returned. For information on creating queries, see the [GoogleSQL\ndocumentation](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax).\n\n\nExample Queries:\n -- Count the number of penguins in each island.\n SELECT island, COUNT(*) AS population\n FROM bigquery-public-data.ml_datasets.penguins GROUP BY island\n\n -- Evaluate a bigquery ML Model.\n SELECT * FROM ML.EVALUATE(MODEL `my_dataset.my_model`)\n\n -- Evaluate BigQuery ML model on custom data\n SELECT * FROM ML.EVALUATE(MODEL `my_dataset.my_model`,\n (SELECT * FROM `my_dataset.my_table`))\n\n -- Predict using BigQuery ML model:\n SELECT * FROM ML.PREDICT(MODEL `my_dataset.my_model`,\n (SELECT * FROM `my_dataset.my_table`))\n\n -- Forecast data using AI.FORECAST\n SELECT * FROM AI.FORECAST(TABLE `project.dataset.my_table`, data_col => 'num_trips',\n timestamp_col => 'date', id_cols => ['usertype'], horizon => 30)\n\nQueries executed using the `execute_sql_readonly` tool will have the job label\n`goog-mcp-server: true` automatically set. Queries are charged to the project specified\n in the `project_id` field.\n", + "inputSchema": { + "description": "Runs a BigQuery SQL query synchronously and returns query results if the query completes within a specified timeout.", + "properties": { + "dryRun": { + "description": "Optional. If set to true, BigQuery doesn't run the job. Instead, if the query is valid, BigQuery returns statistics about the job such as how many bytes would be processed. If the query is invalid, an error returns. The default value is false.", + "type": "boolean" + }, + "projectId": { + "description": "Required. Project that will be used for query execution and billing.", + "type": "string" + }, + "query": { + "description": "Required. The query to execute in the form of a GoogleSQL query.", + "type": "string" + } + }, + "required": [ + "projectId", + "query" + ], + "type": "object" + }, + "name": "execute_sql_readonly", + "outputSchema": { + "$defs": { + "DataPolicyOption": { + "description": "Data policy option. For more information, see [Mask data by applying data policies to a column](https://docs.cloud.google.com/bigquery/docs/column-data-masking#data-policies-on-column).", + "properties": { + "name": { + "description": "Data policy resource name in the form of projects/project_id/locations/location_id/dataPolicies/data_policy_id.", + "type": "string" + } + }, + "type": "object" + }, + "ErrorProto": { + "description": "Error details.", + "properties": { + "debugInfo": { + "description": "Debugging information. This property is internal to Google and should not be used.", + "type": "string" + }, + "location": { + "description": "Specifies where the error occurred, if present.", + "type": "string" + }, + "message": { + "description": "A human-readable description of the error.", + "type": "string" + }, + "reason": { + "description": "A short error code that summarizes the error.", + "type": "string" + } + }, + "type": "object" + }, + "FieldElementType": { + "description": "Represents the type of a field element.", + "properties": { + "type": { + "description": "Required. The type of a field element. For more information, see TableFieldSchema.type.", + "type": "string" + } + }, + "type": "object" + }, + "ForeignTypeInfo": { + "description": "Metadata about the foreign data type definition such as the system in which the type is defined.", + "properties": { + "typeSystem": { + "description": "Required. Specifies the system which defines the foreign data type.", + "enum": [ + "TYPE_SYSTEM_UNSPECIFIED", + "HIVE" + ], + "type": "string", + "x-google-enum-descriptions": [ + "TypeSystem not specified.", + "Represents Hive data types." + ] + } + }, + "type": "object" + }, + "GeneratedColumn": { + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields).", + "properties": { + "generatedExpressionInfo": { + "$ref": "#/$defs/GeneratedExpressionInfo", + "description": "Definition of the expression used to generate the field." + }, + "generatedMode": { + "description": "Optional. Dictates when system generated values are used to populate the field.", + "enum": [ + "GENERATED_MODE_UNSPECIFIED", + "GENERATED_ALWAYS", + "GENERATED_BY_DEFAULT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified GeneratedMode will default to GENERATED_ALWAYS.", + "Field can only have system generated values. Users cannot manually insert values into the field.", + "Use system generated values only if the user does not explicitly provide a value." + ] + } + }, + "type": "object" + }, + "GeneratedExpressionInfo": { + "description": "Definition of the expression used to generate the field.", + "properties": { + "asynchronous": { + "description": "Optional. Whether the column generation is done asynchronously.", + "type": "boolean" + }, + "generationExpression": { + "description": "Optional. The generation expression (e.g. AI.EMBED(...)) used to generated the field.", + "type": "string" + }, + "stored": { + "description": "Optional. Whether the generated column is stored in the table.", + "type": "boolean" + } + }, + "type": "object" + }, + "PolicyTagList": { + "properties": { + "names": { + "description": "A list of policy tag resource names. For example, \"projects/1/locations/eu/taxonomies/2/policyTags/3\". At most 1 policy tag is currently allowed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "TableFieldSchema": { + "description": "A field in TableSchema", + "properties": { + "collation": { + "description": "Optional. Field collation can be set only when the type of field is STRING. The following values are supported: * 'und:ci': undetermined locale, case insensitive. * '': empty string. Default to case-sensitive behavior.", + "type": "string" + }, + "dataPolicies": { + "description": "Optional. Data policies attached to this field, used for field-level access control.", + "items": { + "$ref": "#/$defs/DataPolicyOption" + }, + "type": "array" + }, + "defaultValueExpression": { + "description": "Optional. A SQL expression to specify the [default value] (https://cloud.google.com/bigquery/docs/default-values) for this field.", + "type": "string" + }, + "description": { + "description": "Optional. The field description. The maximum length is 1,024 characters.", + "type": "string" + }, + "fields": { + "description": "Optional. Describes the nested schema fields if the type property is set to RECORD.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeDefinition": { + "description": "Optional. Definition of the foreign data type. Only valid for top-level schema fields (not nested fields). If the type is FOREIGN, this field is required.", + "type": "string" + }, + "generatedColumn": { + "$ref": "#/$defs/GeneratedColumn", + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields)." + }, + "maxLength": { + "description": "Optional. Maximum length of values of this field for STRINGS or BYTES. If max_length is not specified, no maximum length constraint is imposed on this field. If type = \"STRING\", then max_length represents the maximum UTF-8 length of strings in this field. If type = \"BYTES\", then max_length represents the maximum number of bytes in this field. It is invalid to set this field if type ≠ \"STRING\" and ≠ \"BYTES\".", + "format": "int64", + "type": "string" + }, + "mode": { + "description": "Optional. The field mode. Possible values include NULLABLE, REQUIRED and REPEATED. The default value is NULLABLE.", + "type": "string" + }, + "name": { + "description": "Required. The field name. The name must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_), and must start with a letter or underscore. The maximum length is 300 characters.", + "type": "string" + }, + "policyTags": { + "$ref": "#/$defs/PolicyTagList", + "description": "Optional. The policy tags attached to this field, used for field-level access control. If not set, defaults to empty policy_tags." + }, + "precision": { + "description": "Optional. Precision (maximum number of total digits in base 10) and scale (maximum number of digits in the fractional part in base 10) constraints for values of this field for NUMERIC or BIGNUMERIC. It is invalid to set precision or scale if type ≠ \"NUMERIC\" and ≠ \"BIGNUMERIC\". If precision and scale are not specified, no value range constraint is imposed on this field insofar as values are permitted by the type. Values of this NUMERIC or BIGNUMERIC field must be in this range when: * Precision (P) and scale (S) are specified: [-10P-S + 10-S, 10P-S - 10-S] * Precision (P) is specified but not scale (and thus scale is interpreted to be equal to zero): [-10P + 1, 10P - 1]. Acceptable values for precision and scale if both are specified: * If type = \"NUMERIC\": 1 ≤ precision - scale ≤ 29 and 0 ≤ scale ≤ 9. * If type = \"BIGNUMERIC\": 1 ≤ precision - scale ≤ 38 and 0 ≤ scale ≤ 38. Acceptable values for precision if only precision is specified but not scale (and thus scale is interpreted to be equal to zero): * If type = \"NUMERIC\": 1 ≤ precision ≤ 29. * If type = \"BIGNUMERIC\": 1 ≤ precision ≤ 38. If scale is specified but not precision, then it is invalid.", + "format": "int64", + "type": "string" + }, + "rangeElementType": { + "$ref": "#/$defs/FieldElementType", + "description": "Optional. The subtype of the RANGE, if the type of this field is RANGE. If the type is RANGE, this field is required. Values for the field element type can be the following: * DATE * DATETIME * TIMESTAMP" + }, + "roundingMode": { + "description": "Optional. Specifies the rounding mode to be used when storing values of NUMERIC and BIGNUMERIC type.", + "enum": [ + "ROUNDING_MODE_UNSPECIFIED", + "ROUND_HALF_AWAY_FROM_ZERO", + "ROUND_HALF_EVEN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO.", + "ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2", + "ROUND_HALF_EVEN rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2" + ] + }, + "scale": { + "description": "Optional. See documentation for precision.", + "format": "int64", + "type": "string" + }, + "timestampPrecision": { + "default": "6", + "description": "Optional. Precision (maximum number of total digits in base 10) for seconds of TIMESTAMP type. Possible values include: * 6 (Default, for TIMESTAMP type with microsecond precision) * 12 (For TIMESTAMP type with picosecond precision)", + "format": "int64", + "type": "string" + }, + "type": { + "description": "Required. The field data type. Possible values include: * STRING * BYTES * INTEGER (or INT64) * FLOAT (or FLOAT64) * BOOLEAN (or BOOL) * TIMESTAMP * DATE * TIME * DATETIME * GEOGRAPHY * NUMERIC * BIGNUMERIC * JSON * RECORD (or STRUCT) * RANGE Use of RECORD/STRUCT indicates that the field contains a nested schema.", + "type": "string" + } + }, + "type": "object" + }, + "TableSchema": { + "description": "Schema of a table", + "properties": { + "fields": { + "description": "Describes the fields in a table.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeInfo": { + "$ref": "#/$defs/ForeignTypeInfo", + "description": "Optional. Specifies metadata of the foreign data type definition in field schema (TableFieldSchema.foreign_type_definition)." + } + }, + "type": "object" + } + }, + "description": "Response for a BigQuery SQL query.", + "properties": { + "errors": { + "description": "Output only. The first errors or warnings encountered during the running of the job. The final message includes the number of errors that caused the process to stop. Errors here do not necessarily mean that the job has completed or was unsuccessful. For more information about error messages, see [Error messages](https://cloud.google.com/bigquery/docs/error-messages).", + "items": { + "$ref": "#/$defs/ErrorProto" + }, + "readOnly": true, + "type": "array" + }, + "jobComplete": { + "description": "Whether the query has completed or not. If rows or totalRows are present, this will always be true. If this is false, totalRows will not be available.", + "type": "boolean" + }, + "numDmlAffectedRows": { + "description": "Output only. The number of rows affected by a DML statement.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "queryId": { + "description": "Output only. The ID of the query.", + "readOnly": true, + "type": "string" + }, + "rows": { + "description": "An object with as many results as can be contained within the maximum permitted reply size. To get any additional rows, you can call GetQueryResults and specify the jobReference returned above.", + "items": { + "additionalProperties": { + "description": "Properties of the object." + }, + "type": "object" + }, + "type": "array" + }, + "schema": { + "$ref": "#/$defs/TableSchema", + "description": "The schema of the results. Present only when the query completes successfully." + }, + "totalBytesBilled": { + "description": "Output only. The total number of bytes billed for the query. Only applies if the project is configured to use on-demand pricing.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "totalBytesProcessed": { + "description": "Output only. The total number of bytes processed for this query.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "totalSlotMs": { + "description": "Output only. Number of slot ms the user is actually billed for.", + "format": "int64", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false + }, + "description": "Run a SQL query in the project and return the result. Prefer the `execute_sql_readonly`\ntool if possible.\n\nThis tool can execute any query that bigquery supports including:\n* SQL Queries (SELECT, INSERT, UPDATE, DELETE, CREATE, etc.)\n* AI/ML functions like AI.FORECAST, ML.EVALUATE, ML.PREDICT\n* Any other query that bigquery supports.\n\nExample Queries:\n -- Insert data into a table.\n INSERT INTO `my_project.my_dataset`.my_table (name, age)\n VALUES ('Alice', 30);\n\n -- Create a table.\n CREATE TABLE `my_project.my_dataset`.my_table (\n name STRING,\n age INT64);\n\n -- DELETE data from a table.\n DELETE FROM `my_project.my_dataset`.my_table WHERE name = 'Alice';\n\n -- Create Dataset\n CREATE SCHEMA `my_project.my_dataset` OPTIONS (location = 'US');\n\n -- Drop table\n DROP TABLE `my_project.my_dataset`.my_table;\n\n -- Drop dataset\n DROP SCHEMA `my_project.my_dataset`;\n\n -- Create Model\n CREATE OR REPLACE MODEL `my_project.my_dataset.my_model`\n OPTIONS (\n model_type = 'LINEAR_REG'\n LS_INIT_LEARN_RATE=0.15,\n L1_REG=1,\n MAX_ITERATIONS=5,\n DATA_SPLIT_METHOD='SEQ',\n DATA_SPLIT_EVAL_FRACTION=0.3,\n DATA_SPLIT_COL='timestamp') AS\n SELECT col1, col2, timestamp, label FROM `my_project.my_dataset.my_table`;\n\nQueries executed using the `execute_sql` tool will have the job label\n`goog-mcp-server: true` automatically set. Queries are charged to the project specified\n in the `project_id` field.\n", + "inputSchema": { + "description": "Runs a BigQuery SQL query synchronously and returns query results if the query completes within a specified timeout.", + "properties": { + "dryRun": { + "description": "Optional. If set to true, BigQuery doesn't run the job. Instead, if the query is valid, BigQuery returns statistics about the job such as how many bytes would be processed. If the query is invalid, an error returns. The default value is false.", + "type": "boolean" + }, + "projectId": { + "description": "Required. Project that will be used for query execution and billing.", + "type": "string" + }, + "query": { + "description": "Required. The query to execute in the form of a GoogleSQL query.", + "type": "string" + } + }, + "required": [ + "projectId", + "query" + ], + "type": "object" + }, + "name": "execute_sql", + "outputSchema": { + "$defs": { + "DataPolicyOption": { + "description": "Data policy option. For more information, see [Mask data by applying data policies to a column](https://docs.cloud.google.com/bigquery/docs/column-data-masking#data-policies-on-column).", + "properties": { + "name": { + "description": "Data policy resource name in the form of projects/project_id/locations/location_id/dataPolicies/data_policy_id.", + "type": "string" + } + }, + "type": "object" + }, + "ErrorProto": { + "description": "Error details.", + "properties": { + "debugInfo": { + "description": "Debugging information. This property is internal to Google and should not be used.", + "type": "string" + }, + "location": { + "description": "Specifies where the error occurred, if present.", + "type": "string" + }, + "message": { + "description": "A human-readable description of the error.", + "type": "string" + }, + "reason": { + "description": "A short error code that summarizes the error.", + "type": "string" + } + }, + "type": "object" + }, + "FieldElementType": { + "description": "Represents the type of a field element.", + "properties": { + "type": { + "description": "Required. The type of a field element. For more information, see TableFieldSchema.type.", + "type": "string" + } + }, + "type": "object" + }, + "ForeignTypeInfo": { + "description": "Metadata about the foreign data type definition such as the system in which the type is defined.", + "properties": { + "typeSystem": { + "description": "Required. Specifies the system which defines the foreign data type.", + "enum": [ + "TYPE_SYSTEM_UNSPECIFIED", + "HIVE" + ], + "type": "string", + "x-google-enum-descriptions": [ + "TypeSystem not specified.", + "Represents Hive data types." + ] + } + }, + "type": "object" + }, + "GeneratedColumn": { + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields).", + "properties": { + "generatedExpressionInfo": { + "$ref": "#/$defs/GeneratedExpressionInfo", + "description": "Definition of the expression used to generate the field." + }, + "generatedMode": { + "description": "Optional. Dictates when system generated values are used to populate the field.", + "enum": [ + "GENERATED_MODE_UNSPECIFIED", + "GENERATED_ALWAYS", + "GENERATED_BY_DEFAULT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified GeneratedMode will default to GENERATED_ALWAYS.", + "Field can only have system generated values. Users cannot manually insert values into the field.", + "Use system generated values only if the user does not explicitly provide a value." + ] + } + }, + "type": "object" + }, + "GeneratedExpressionInfo": { + "description": "Definition of the expression used to generate the field.", + "properties": { + "asynchronous": { + "description": "Optional. Whether the column generation is done asynchronously.", + "type": "boolean" + }, + "generationExpression": { + "description": "Optional. The generation expression (e.g. AI.EMBED(...)) used to generated the field.", + "type": "string" + }, + "stored": { + "description": "Optional. Whether the generated column is stored in the table.", + "type": "boolean" + } + }, + "type": "object" + }, + "PolicyTagList": { + "properties": { + "names": { + "description": "A list of policy tag resource names. For example, \"projects/1/locations/eu/taxonomies/2/policyTags/3\". At most 1 policy tag is currently allowed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "TableFieldSchema": { + "description": "A field in TableSchema", + "properties": { + "collation": { + "description": "Optional. Field collation can be set only when the type of field is STRING. The following values are supported: * 'und:ci': undetermined locale, case insensitive. * '': empty string. Default to case-sensitive behavior.", + "type": "string" + }, + "dataPolicies": { + "description": "Optional. Data policies attached to this field, used for field-level access control.", + "items": { + "$ref": "#/$defs/DataPolicyOption" + }, + "type": "array" + }, + "defaultValueExpression": { + "description": "Optional. A SQL expression to specify the [default value] (https://cloud.google.com/bigquery/docs/default-values) for this field.", + "type": "string" + }, + "description": { + "description": "Optional. The field description. The maximum length is 1,024 characters.", + "type": "string" + }, + "fields": { + "description": "Optional. Describes the nested schema fields if the type property is set to RECORD.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeDefinition": { + "description": "Optional. Definition of the foreign data type. Only valid for top-level schema fields (not nested fields). If the type is FOREIGN, this field is required.", + "type": "string" + }, + "generatedColumn": { + "$ref": "#/$defs/GeneratedColumn", + "description": "Optional. Definition of how values are generated for the field. Only valid for top-level schema fields (not nested fields)." + }, + "maxLength": { + "description": "Optional. Maximum length of values of this field for STRINGS or BYTES. If max_length is not specified, no maximum length constraint is imposed on this field. If type = \"STRING\", then max_length represents the maximum UTF-8 length of strings in this field. If type = \"BYTES\", then max_length represents the maximum number of bytes in this field. It is invalid to set this field if type ≠ \"STRING\" and ≠ \"BYTES\".", + "format": "int64", + "type": "string" + }, + "mode": { + "description": "Optional. The field mode. Possible values include NULLABLE, REQUIRED and REPEATED. The default value is NULLABLE.", + "type": "string" + }, + "name": { + "description": "Required. The field name. The name must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_), and must start with a letter or underscore. The maximum length is 300 characters.", + "type": "string" + }, + "policyTags": { + "$ref": "#/$defs/PolicyTagList", + "description": "Optional. The policy tags attached to this field, used for field-level access control. If not set, defaults to empty policy_tags." + }, + "precision": { + "description": "Optional. Precision (maximum number of total digits in base 10) and scale (maximum number of digits in the fractional part in base 10) constraints for values of this field for NUMERIC or BIGNUMERIC. It is invalid to set precision or scale if type ≠ \"NUMERIC\" and ≠ \"BIGNUMERIC\". If precision and scale are not specified, no value range constraint is imposed on this field insofar as values are permitted by the type. Values of this NUMERIC or BIGNUMERIC field must be in this range when: * Precision (P) and scale (S) are specified: [-10P-S + 10-S, 10P-S - 10-S] * Precision (P) is specified but not scale (and thus scale is interpreted to be equal to zero): [-10P + 1, 10P - 1]. Acceptable values for precision and scale if both are specified: * If type = \"NUMERIC\": 1 ≤ precision - scale ≤ 29 and 0 ≤ scale ≤ 9. * If type = \"BIGNUMERIC\": 1 ≤ precision - scale ≤ 38 and 0 ≤ scale ≤ 38. Acceptable values for precision if only precision is specified but not scale (and thus scale is interpreted to be equal to zero): * If type = \"NUMERIC\": 1 ≤ precision ≤ 29. * If type = \"BIGNUMERIC\": 1 ≤ precision ≤ 38. If scale is specified but not precision, then it is invalid.", + "format": "int64", + "type": "string" + }, + "rangeElementType": { + "$ref": "#/$defs/FieldElementType", + "description": "Optional. The subtype of the RANGE, if the type of this field is RANGE. If the type is RANGE, this field is required. Values for the field element type can be the following: * DATE * DATETIME * TIMESTAMP" + }, + "roundingMode": { + "description": "Optional. Specifies the rounding mode to be used when storing values of NUMERIC and BIGNUMERIC type.", + "enum": [ + "ROUNDING_MODE_UNSPECIFIED", + "ROUND_HALF_AWAY_FROM_ZERO", + "ROUND_HALF_EVEN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO.", + "ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2", + "ROUND_HALF_EVEN rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2" + ] + }, + "scale": { + "description": "Optional. See documentation for precision.", + "format": "int64", + "type": "string" + }, + "timestampPrecision": { + "default": "6", + "description": "Optional. Precision (maximum number of total digits in base 10) for seconds of TIMESTAMP type. Possible values include: * 6 (Default, for TIMESTAMP type with microsecond precision) * 12 (For TIMESTAMP type with picosecond precision)", + "format": "int64", + "type": "string" + }, + "type": { + "description": "Required. The field data type. Possible values include: * STRING * BYTES * INTEGER (or INT64) * FLOAT (or FLOAT64) * BOOLEAN (or BOOL) * TIMESTAMP * DATE * TIME * DATETIME * GEOGRAPHY * NUMERIC * BIGNUMERIC * JSON * RECORD (or STRUCT) * RANGE Use of RECORD/STRUCT indicates that the field contains a nested schema.", + "type": "string" + } + }, + "type": "object" + }, + "TableSchema": { + "description": "Schema of a table", + "properties": { + "fields": { + "description": "Describes the fields in a table.", + "items": { + "$ref": "#/$defs/TableFieldSchema" + }, + "type": "array" + }, + "foreignTypeInfo": { + "$ref": "#/$defs/ForeignTypeInfo", + "description": "Optional. Specifies metadata of the foreign data type definition in field schema (TableFieldSchema.foreign_type_definition)." + } + }, + "type": "object" + } + }, + "description": "Response for a BigQuery SQL query.", + "properties": { + "errors": { + "description": "Output only. The first errors or warnings encountered during the running of the job. The final message includes the number of errors that caused the process to stop. Errors here do not necessarily mean that the job has completed or was unsuccessful. For more information about error messages, see [Error messages](https://cloud.google.com/bigquery/docs/error-messages).", + "items": { + "$ref": "#/$defs/ErrorProto" + }, + "readOnly": true, + "type": "array" + }, + "jobComplete": { + "description": "Whether the query has completed or not. If rows or totalRows are present, this will always be true. If this is false, totalRows will not be available.", + "type": "boolean" + }, + "numDmlAffectedRows": { + "description": "Output only. The number of rows affected by a DML statement.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "queryId": { + "description": "Output only. The ID of the query.", + "readOnly": true, + "type": "string" + }, + "rows": { + "description": "An object with as many results as can be contained within the maximum permitted reply size. To get any additional rows, you can call GetQueryResults and specify the jobReference returned above.", + "items": { + "additionalProperties": { + "description": "Properties of the object." + }, + "type": "object" + }, + "type": "array" + }, + "schema": { + "$ref": "#/$defs/TableSchema", + "description": "The schema of the results. Present only when the query completes successfully." + }, + "totalBytesBilled": { + "description": "Output only. The total number of bytes billed for the query. Only applies if the project is configured to use on-demand pricing.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "totalBytesProcessed": { + "description": "Output only. The total number of bytes processed for this query.", + "format": "int64", + "readOnly": true, + "type": "string" + }, + "totalSlotMs": { + "description": "Output only. Number of slot ms the user is actually billed for.", + "format": "int64", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + } + } + ] +} diff --git a/google-bigquery-official/server/tools/index.ts b/google-bigquery-official/server/tools/index.ts new file mode 100644 index 00000000..bb02c05e --- /dev/null +++ b/google-bigquery-official/server/tools/index.ts @@ -0,0 +1,9 @@ +import { wrapBackendSnapshot } from "@decocms/mcps-shared/google-mcp"; +import { BACKEND_URL, TOOL_SNAPSHOT } from "../constants.ts"; +import { getGoogleAccessToken } from "../lib/env.ts"; +import type { Env } from "../../shared/deco.gen.ts"; + +export const tools = wrapBackendSnapshot(TOOL_SNAPSHOT.tools, { + backendUrl: BACKEND_URL, + getAccessToken: (env) => getGoogleAccessToken(env as Env), +}); diff --git a/google-bigquery-official/shared/deco.gen.ts b/google-bigquery-official/shared/deco.gen.ts new file mode 100644 index 00000000..c535e482 --- /dev/null +++ b/google-bigquery-official/shared/deco.gen.ts @@ -0,0 +1,7 @@ +import { type DefaultEnv } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { z } from "zod"; + +export const StateSchema = z.object({}); + +export type Env = DefaultEnv; diff --git a/google-bigquery-official/tsconfig.json b/google-bigquery-official/tsconfig.json new file mode 100644 index 00000000..f715197c --- /dev/null +++ b/google-bigquery-official/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "ES2024", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowJs": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["server", "shared"] +} diff --git a/google-gke-official/README.md b/google-gke-official/README.md index 90d436d7..c89b2075 100644 --- a/google-gke-official/README.md +++ b/google-gke-official/README.md @@ -1,6 +1,14 @@ # Google GKE MCP Server Official -This is the **official Google GKE MCP Server**, provided directly by the Google Cloud team for integration with Google Kubernetes Engine. +Thin wrapper around Google's official GKE MCP server (`container.googleapis.com/mcp`). The wrapper holds the Google OAuth client credentials server-side and proxies JSON-RPC `tools/call` to the upstream — Google's MCP doesn't support Dynamic Client Registration, so this layer is needed to integrate it into mesh. + +See [`TOOLS.md`](./TOOLS.md) for the catalog (auto-generated via `bun run generate-tools`). Refresh after Google updates the upstream tools/list. + +The OAuth + proxy plumbing lives in `@decocms/mcps-shared/google-mcp` — same code path used by `google-workspace` and the other `google-*-official` wrappers. + +--- + +The upstream **Google GKE MCP Server** is provided directly by the Google Cloud team for integration with Google Kubernetes Engine. ## About Google GKE diff --git a/google-gke-official/TOOLS.md b/google-gke-official/TOOLS.md new file mode 100644 index 00000000..4f54eba4 --- /dev/null +++ b/google-gke-official/TOOLS.md @@ -0,0 +1,35 @@ +# Google GKE — Tool Catalog + +_Auto-generated by `bun run generate-tools`. Do not edit by hand._ + +23 tools across 1 backend, gated behind 1 OAuth scope. + +**Endpoint:** `https://container.googleapis.com/mcp` + +**Scopes:** +- `https://www.googleapis.com/auth/container` + +**Tools (23):** +- `list_k8s_api_resources` — Retrieves the available API groups and resources from a Kubernetes cluster. This is similar to running `kubectl api-resources`. +- `check_k8s_auth` — Checks whether an action is allowed on a Kubernetes resource. This is similar to running `kubectl auth can-i`. +- `describe_k8s_resource` — Shows the details of a specific Kubernetes resource. This is similar to running `kubectl describe`. +- `list_k8s_events` — Retrieves events from a Kubernetes cluster. This is similar to running `kubectl events`. +- `get_k8s_resource` — Gets one or more Kubernetes resources from a cluster. Resources can be filtered by type, name, namespace, and label selectors. Returns the resources in YAML format. This is similar to running `kubectl get`. +- `get_k8s_cluster_info` — Gets cluster endpoint information. This is similar to running `kubectl cluster-info`. +- `get_k8s_version` — Retrieves Kubernetes client and server versions for a given cluster. This is similar to running `kubectl version`. +- `get_k8s_rollout_status` — Checks the current rollout status of a Kubernetes resource. This is similar to running `kubectl rollout status`. +- `list_clusters` — Lists GKE clusters in a given project and location. Location can be a region, zone, or '-' for all locations. +- `create_cluster` — Creates a new GKE cluster in a given project and location. It's recommended to read the [GKE documentation](https://docs.cloud.google.com/kubernetes-engine/docs/concepts/configuration-overview) to understand cluster configuration options. Cluster creation will default to Autopilot mode, as recommended by GKE best practices. If the user explicitly wants to create a Standard cluster, you need to set autopilot.enabled=false in the cluster configuration. This is similar to running `gcloud container clusters create-auto` or `gcloud container clusters create`. +- `update_cluster` — Updates a specific GKE cluster. +- `get_cluster` — Gets the details of a specific GKE cluster. +- `list_operations` — Lists GKE operations in a given project and location. Location can be a region, zone, or '-' for all locations. +- `get_operation` — Gets the details of a specific GKE operation. +- `cancel_operation` — Cancels a specific GKE operation. +- `create_node_pool` — Creates a node pool for a specific GKE cluster. +- `list_node_pools` — Lists the node pools for a specific GKE cluster. +- `get_node_pool` — Gets the details of a specific node pool within a GKE cluster. +- `update_node_pool` — Updates a specific node pool within a GKE cluster. +- `get_k8s_logs` — Gets logs from a Kubernetes container in a pod. This is similar to running `kubectl logs`. +- `apply_k8s_manifest` — Applies a Kubernetes manifest to a cluster using server-side apply. This is similar to running `kubectl apply --server-side`. +- `delete_k8s_resource` — Deletes a Kubernetes resource from a cluster. This is similar to running `kubectl delete`. +- `patch_k8s_resource` — Patches a Kubernetes resource. This is similar to running `kubectl patch`. diff --git a/google-gke-official/app.json b/google-gke-official/app.json index 3579fdbe..c446be1b 100644 --- a/google-gke-official/app.json +++ b/google-gke-official/app.json @@ -4,7 +4,7 @@ "friendlyName": "Google GKE", "connection": { "type": "HTTP", - "url": "https://container.googleapis.com/mcp" + "url": "https://sites-google-gke-official.decocache.com/mcp" }, "description": "Manage Kubernetes clusters on Google Cloud with GKE. Deploy, scale, and monitor containerized applications through natural language commands.", "icon": "https://www.gstatic.com/images/branding/product/2x/gke_64dp.png", @@ -12,7 +12,7 @@ "metadata": { "categories": ["Developer Tools"], "official": true, - "mesh_unlisted": true, + "mesh_unlisted": false, "tags": ["kubernetes", "containers", "docker", "orchestration", "google-cloud", "gke", "devops", "microservices", "cloud-native"], "short_description": "Manage Kubernetes clusters on Google Cloud with GKE", "mesh_description": "Google Kubernetes Engine (GKE) is a managed Kubernetes service that simplifies deploying, managing, and scaling containerized applications using Google's infrastructure. This official MCP enables you to create and manage GKE clusters with autopilot or standard modes, configure node pools with custom machine types, and implement cluster autoscaling based on workload demands. Deploy applications using Kubernetes manifests, Helm charts, or Kustomize configurations. Manage pods, deployments, services, ingress, config maps, and secrets through intuitive natural language commands. Configure networking with VPC-native clusters, private clusters, and workload identity for secure service authentication. Set up horizontal pod autoscaling, vertical pod autoscaling, and cluster autoscaler for optimal resource utilization. Implement CI/CD pipelines with GKE integration, rolling updates with zero downtime, and canary deployments for gradual rollouts. Monitor cluster health, resource usage, and application performance with Cloud Monitoring and Logging integration. Configure security policies with Pod Security Standards, Binary Authorization, and network policies. Manage multi-cluster deployments with GKE Enterprise, implement service mesh with Anthos Service Mesh, and use Config Sync for GitOps workflows. Perfect for teams running cloud-native applications at scale." diff --git a/google-gke-official/package.json b/google-gke-official/package.json new file mode 100644 index 00000000..e90d09f7 --- /dev/null +++ b/google-gke-official/package.json @@ -0,0 +1,32 @@ +{ + "name": "google-gke-official", + "version": "1.0.0", + "description": "Wrapper around Google's official GKE MCP server (container.googleapis.com/mcp)", + "private": true, + "type": "module", + "scripts": { + "dev": "bun run --hot server/main.ts", + "build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js", + "build": "bun run build:server", + "publish": "cat app.json | deco registry publish -w /shared/deco -y", + "check": "tsc --noEmit", + "generate-tools": "bun run server/scripts/generate-tools.ts" + }, + "exports": { + "./tools": "./server/tools/index.ts", + "./constants": "./server/constants.ts" + }, + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0" + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/google-gke-official/server/constants.ts b/google-gke-official/server/constants.ts new file mode 100644 index 00000000..b4517888 --- /dev/null +++ b/google-gke-official/server/constants.ts @@ -0,0 +1,15 @@ +import snapshot from "./tools/generated/gke.json" with { type: "json" }; +import type { BackendToolDefinition } from "@decocms/mcps-shared/google-mcp"; + +export const BACKEND_URL = "https://container.googleapis.com/mcp"; + +export interface BackendSnapshot { + service: string; + url: string; + scopes: string[]; + tools: BackendToolDefinition[]; +} + +export const TOOL_SNAPSHOT = snapshot as BackendSnapshot; + +export const SCOPES: string[] = TOOL_SNAPSHOT.scopes; diff --git a/google-gke-official/server/lib/env.ts b/google-gke-official/server/lib/env.ts new file mode 100644 index 00000000..8618a5e3 --- /dev/null +++ b/google-gke-official/server/lib/env.ts @@ -0,0 +1,9 @@ +import type { Env } from "../../shared/deco.gen.ts"; + +export const getGoogleAccessToken = (env: Env): string => { + const authorization = env.MESH_REQUEST_CONTEXT?.authorization; + if (!authorization) { + throw new Error("Not authenticated. Please authorize Google GKE first."); + } + return authorization; +}; diff --git a/google-gke-official/server/main.ts b/google-gke-official/server/main.ts new file mode 100644 index 00000000..024c1b75 --- /dev/null +++ b/google-gke-official/server/main.ts @@ -0,0 +1,21 @@ +import { withRuntime } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { serve } from "@decocms/mcps-shared/serve"; +import { createGoogleOAuth } from "@decocms/mcps-shared/google-oauth"; + +import { tools } from "./tools/index.ts"; +import { SCOPES } from "./constants.ts"; +import { type Env, StateSchema } from "../shared/deco.gen.ts"; + +export type { Env }; + +const runtime = withRuntime({ + configuration: { + scopes: [], + state: StateSchema, + }, + tools: (env: Env) => tools.map((createTool) => createTool(env)), + oauth: createGoogleOAuth({ scopes: SCOPES }), +}); + +serve(runtime.fetch); diff --git a/google-gke-official/server/scripts/generate-tools.ts b/google-gke-official/server/scripts/generate-tools.ts new file mode 100644 index 00000000..6f028a58 --- /dev/null +++ b/google-gke-official/server/scripts/generate-tools.ts @@ -0,0 +1,16 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { generateSnapshots } from "@decocms/mcps-shared/google-mcp/generate-snapshot"; +import { BACKEND_URL } from "../constants.ts"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const OUT_DIR = join(HERE, "..", "tools", "generated"); +const PACKAGE_ROOT = join(HERE, "..", ".."); +const TOOLS_MD_PATH = join(PACKAGE_ROOT, "TOOLS.md"); + +await generateSnapshots({ + title: "Google GKE — Tool Catalog", + outDir: OUT_DIR, + toolsMarkdownPath: TOOLS_MD_PATH, + backends: [{ service: "gke", url: BACKEND_URL }], +}); diff --git a/google-gke-official/server/tools/generated/_index.json b/google-gke-official/server/tools/generated/_index.json new file mode 100644 index 00000000..6396923e --- /dev/null +++ b/google-gke-official/server/tools/generated/_index.json @@ -0,0 +1,32 @@ +{ + "gke": { + "scopes": [ + "https://www.googleapis.com/auth/container" + ], + "toolNames": [ + "list_k8s_api_resources", + "check_k8s_auth", + "describe_k8s_resource", + "list_k8s_events", + "get_k8s_resource", + "get_k8s_cluster_info", + "get_k8s_version", + "get_k8s_rollout_status", + "list_clusters", + "create_cluster", + "update_cluster", + "get_cluster", + "list_operations", + "get_operation", + "cancel_operation", + "create_node_pool", + "list_node_pools", + "get_node_pool", + "update_node_pool", + "get_k8s_logs", + "apply_k8s_manifest", + "delete_k8s_resource", + "patch_k8s_resource" + ] + } +} diff --git a/google-gke-official/server/tools/generated/gke.json b/google-gke-official/server/tools/generated/gke.json new file mode 100644 index 00000000..7e4e769d --- /dev/null +++ b/google-gke-official/server/tools/generated/gke.json @@ -0,0 +1,1825 @@ +{ + "service": "gke", + "url": "https://container.googleapis.com/mcp", + "scopes": [ + "https://www.googleapis.com/auth/container" + ], + "tools": [ + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Retrieves the available API groups and resources from a Kubernetes cluster. This is similar to running `kubectl api-resources`.", + "inputSchema": { + "description": "Request for retrieving Kubernetes API resources.", + "properties": { + "parent": { + "description": "Required. The cluster, which owns this collection of resource types. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "list_k8s_api_resources", + "outputSchema": { + "$defs": { + "APIGroupDiscovery": { + "description": "APIGroupDiscovery is a discovery document for an API group.", + "properties": { + "name": { + "description": "The name of the resource type. e.g. \"pods\", \"deployments\", \"services\".", + "type": "string" + }, + "preferredVersion": { + "description": "The preferred version for this API group, in the form {group}/{version}.", + "type": "string" + }, + "versions": { + "description": "The list of versions for this API group, in the form {group}/{version}.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "APIGroupDiscoveryList is a list of API group discovery.", + "properties": { + "errors": { + "description": "Errors encountered during discovery.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "groups": { + "description": "The list of API group discovery.", + "items": { + "$ref": "#/$defs/APIGroupDiscovery" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Checks whether an action is allowed on a Kubernetes resource. This is similar to running `kubectl auth can-i`.", + "inputSchema": { + "description": "Request for checking authorization for a Kubernetes resource.", + "properties": { + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, \"default\" is used for namespace-scoped resources.", + "type": "string" + }, + "parent": { + "description": "Required. The cluster to check authorization against. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "resource": { + "description": "Optional. The name of the resource to check.", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of resource to check. e.g. \"pods\", \"deployments\", \"services\".", + "type": "string" + }, + "verb": { + "description": "Required. The verb to check. e.g. \"get\", \"list\", \"watch\", \"create\", \"update\", \"patch\", \"delete\".", + "type": "string" + } + }, + "required": [ + "parent", + "verb", + "resourceType" + ], + "type": "object" + }, + "name": "check_k8s_auth", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for checking authorization for a Kubernetes resource.", + "properties": { + "errors": { + "description": "Errors encountered during auth check.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "result": { + "description": "The result of auth can-i check.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Shows the details of a specific Kubernetes resource. This is similar to running `kubectl describe`.", + "inputSchema": { + "description": "Request message for KubeDescribe.", + "properties": { + "labelSelector": { + "description": "Optional. A label selector to filter resources.", + "type": "string" + }, + "name": { + "description": "Optional. The name of the resource.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource.", + "type": "string" + }, + "parent": { + "description": "Required. The parent cluster.", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of the resource.", + "type": "string" + } + }, + "required": [ + "parent", + "resourceType" + ], + "type": "object" + }, + "name": "describe_k8s_resource", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response message for KubeDescribe.", + "properties": { + "description": { + "description": "The description of the resource.", + "type": "string" + }, + "errors": { + "description": "Errors encountered during description retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Retrieves events from a Kubernetes cluster. This is similar to running `kubectl events`.", + "inputSchema": { + "description": "Request message for KubeEvents.", + "properties": { + "allNamespaces": { + "description": "Optional. If true, retrieve events from all namespaces.", + "type": "boolean" + }, + "limit": { + "description": "Optional. The maximum number of events to return. If not specified, 500 is used.", + "format": "int64", + "type": "string" + }, + "name": { + "description": "Optional. The name of the resource to retrieve events for.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified and all_namespaces is false, \"default\" is used.", + "type": "string" + }, + "parent": { + "description": "Required. The parent cluster. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "resourceType": { + "description": "Optional. The type of the resource to retrieve events for.", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "list_k8s_events", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response message for KubeEvents.", + "properties": { + "errors": { + "description": "Errors encountered during events retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "events": { + "description": "The events in string format.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets one or more Kubernetes resources from a cluster. Resources can be filtered by type, name, namespace, and label selectors. Returns the resources in YAML format. This is similar to running `kubectl get`.", + "inputSchema": { + "description": "Request for retrieving Kubernetes resources.", + "properties": { + "customColumns": { + "description": "Optional. The field mask to specify columns to display. Use a single \"*\" to get all fields. When both custom_columns and output_format are specified, output_format is ignored.", + "format": "google-fieldmask", + "pattern": "^(\\s*[^,\\s.]+(\\s*[,.]\\s*[^,\\s.]+)*)?$", + "type": "string" + }, + "fieldSelector": { + "description": "Optional. A field selector to filter resources.", + "type": "string" + }, + "labelSelector": { + "description": "Optional. A label selector to filter resources.", + "type": "string" + }, + "name": { + "description": "Optional. The name of the resource to retrieve. If not specified, all resources of the given type are returned.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, all namespaces are searched.", + "type": "string" + }, + "outputFormat": { + "description": "Optional. The output format. One of: (table, wide, yaml, json). If not specified, defaults to table. When both custom_columns and output_format are specified, output_format is ignored.", + "enum": [ + "OUTPUT_FORMAT_UNSPECIFIED", + "TABLE", + "WIDE", + "YAML", + "JSON" + ], + "type": "string", + "x-google-enum-descriptions": [ + "If not specified, defaults to table.", + "Output in table format.", + "Output in wide table format.", + "Output in yaml format.", + "Output in json format." + ] + }, + "parent": { + "description": "Required. The cluster, which owns this collection of resources. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of resource to retrieve. Kubernetes resource/kind name in singular form, lower case. e.g. \"pod\", \"deployment\", \"service\".", + "type": "string" + } + }, + "required": [ + "parent", + "resourceType" + ], + "type": "object" + }, + "name": "get_k8s_resource", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for retrieving Kubernetes resources.", + "properties": { + "errors": { + "description": "Errors encountered during retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "output": { + "description": "The output of the command in the requested format. It may contain resources in YAML or JSON format, or a table in plain text, or errors.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets cluster endpoint information. This is similar to running `kubectl cluster-info`.", + "inputSchema": { + "description": "Request message for KubeClusterInfo.", + "properties": { + "parent": { + "description": "Required. The parent cluster. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "get_k8s_cluster_info", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response message for KubeClusterInfo.", + "properties": { + "clusterInfo": { + "description": "The cluster info of the resource. Displays address and port information about the Kubernetes control plane and other services running within the cluster such as CoreDNS and Metrics-server. Example: \"The Kubernetes control plane is at 10.128.0.10:6443. CoreDNS is running at https://192.0.2.1:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy Metrics-server is running at 127.0.0.1:8080\"", + "type": "string" + }, + "errors": { + "description": "Errors encountered during cluster info retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Retrieves Kubernetes client and server versions for a given cluster. This is similar to running `kubectl version`.", + "inputSchema": { + "description": "KubeVersionRequest is the request message for retrieving Kubernetes server versions.", + "properties": { + "parent": { + "description": "Required. The cluster to get version information from, in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "get_k8s_version", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "KubeVersionResponse is the response message for retrieving Kubernetes server version.", + "properties": { + "errors": { + "description": "Errors encountered during version retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "serverVersion": { + "description": "The server version.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Checks the current rollout status of a Kubernetes resource. This is similar to running `kubectl rollout status`.", + "inputSchema": { + "description": "Request for checking rollout status of a Kubernetes resource.", + "properties": { + "name": { + "description": "Required. The name of the resource to check.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, \"default\" is used for namespace-scoped resources.", + "type": "string" + }, + "parent": { + "description": "Required. The cluster to check rollout status in. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of resource to check. e.g. \"deployment\", \"daemonset\", \"statefulset\".", + "type": "string" + } + }, + "required": [ + "parent", + "resourceType", + "name" + ], + "type": "object" + }, + "name": "get_k8s_rollout_status", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for checking rollout status of a Kubernetes resource.", + "properties": { + "errors": { + "description": "Errors encountered during rollout status check.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "result": { + "description": "The result of the rollout status check.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Lists GKE clusters in a given project and location. Location can be a region, zone, or '-' for all locations.", + "inputSchema": { + "description": "MCPListClustersRequest lists clusters.", + "properties": { + "parent": { + "description": "Required. The parent (project and location) where the clusters will be listed. Specified in the format `projects/*/locations/*`. Location \"-\" matches all zones and all regions.", + "type": "string" + }, + "readMask": { + "description": "Optional. The field mask to specify the fields to be returned in the response. Use a single \"*\" to get all fields. Default: clusters.autopilot,clusters.createTime,clusters.currentMasterVersion,clusters.currentNodeCount,clusters.currentNodeVersion,clusters.description,clusters.endpoint,clusters.fleet,clusters.location,clusters.name,clusters.network,clusters.nodePools.name,clusters.releaseChannel,clusters.resourceLabels,clusters.selfLink,clusters.status,clusters.statusMessage,clusters.subnetwork,missingZones.", + "format": "google-fieldmask", + "pattern": "^(\\s*[^,\\s.]+(\\s*[,.]\\s*[^,\\s.]+)*)?$", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "list_clusters", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for listing clusters in JSON format. The full cluster object can be found at https://cloud.google.com/container-engine/reference/rest/v1/projects.locations.clusters.", + "properties": { + "clusters": { + "description": "A string representing the ListClustersResponse object.", + "items": { + "type": "string" + }, + "type": "array" + }, + "errors": { + "description": "Errors encountered during cluster listing.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Creates a new GKE cluster in a given project and location. It's recommended to read the [GKE documentation](https://docs.cloud.google.com/kubernetes-engine/docs/concepts/configuration-overview) to understand cluster configuration options. Cluster creation will default to Autopilot mode, as recommended by GKE best practices. If the user explicitly wants to create a Standard cluster, you need to set autopilot.enabled=false in the cluster configuration. This is similar to running `gcloud container clusters create-auto` or `gcloud container clusters create`.\n", + "inputSchema": { + "description": "MCPCreateClusterRequest creates a new cluster.", + "properties": { + "cluster": { + "description": "Required. A [cluster resource](https://cloud.google.com/container-engine/reference/rest/v1/projects.locations.clusters) represented as a string using JSON format.", + "type": "string" + }, + "parent": { + "description": "Required. The parent (project and location) where the cluster will be created. Specified in the format `projects/*/locations/*`.", + "type": "string" + } + }, + "required": [ + "parent", + "cluster" + ], + "type": "object" + }, + "name": "create_cluster", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPOperation wraps the GKE Operation with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during the operation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operation": { + "description": "JSON string of the GKE Operation object. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Updates a specific GKE cluster.", + "inputSchema": { + "description": "MCPUpdateClusterRequest updates a cluster.", + "properties": { + "name": { + "description": "Required. The name (project, location, cluster) of the cluster to update. Specified in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + }, + "update": { + "description": "Required. A description of the update represented as a string using JSON format. The full update request object can be found at https://cloud.google.com/container-engine/reference/rest/v1/projects.locations.clusters/update https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/ClusterUpdate", + "type": "string" + } + }, + "required": [ + "name", + "update" + ], + "type": "object" + }, + "name": "update_cluster", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPOperation wraps the GKE Operation with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during the operation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operation": { + "description": "JSON string of the GKE Operation object. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets the details of a specific GKE cluster.", + "inputSchema": { + "description": "MCPGetClusterRequest gets the settings of a cluster.", + "properties": { + "name": { + "description": "Required. The name (project, location, cluster) of the cluster to retrieve. Specified in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + }, + "readMask": { + "description": "Optional. The field mask to specify the fields to be returned in the response. Use a single \"*\" to get all fields. Default: autopilot,createTime,currentMasterVersion,currentNodeCount,currentNodeVersion,description,endpoint,fleet,location,name,network,nodePools.locations,nodePools.name,nodePools.status,nodePools.version,releaseChannel,resourceLabels,selfLink,status,statusMessage,subnetwork.", + "format": "google-fieldmask", + "pattern": "^(\\s*[^,\\s.]+(\\s*[,.]\\s*[^,\\s.]+)*)?$", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "name": "get_cluster", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for returned cluster object in JSON format. The full cluster object can be found at https://cloud.google.com/container-engine/reference/rest/v1/projects.locations.clusters.", + "properties": { + "cluster": { + "description": "A string representing the Cluster object in JSON format.", + "type": "string" + }, + "errors": { + "description": "Errors encountered during cluster retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Lists GKE operations in a given project and location. Location can be a region, zone, or '-' for all locations.", + "inputSchema": { + "description": "MCPListOperationsRequest lists operations.", + "properties": { + "parent": { + "description": "Required. The parent (project and location) where the operations will be listed. Specified in the format `projects/*/locations/*`. Location \"-\" matches all zones and all regions.", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "list_operations", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPListOperationsResponse wraps the list of operations with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during operations listing.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operations": { + "description": "A list of JSON strings of GKE Operation objects. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets the details of a specific GKE operation.", + "inputSchema": { + "description": "MCPGetOperationRequest gets a single operation.", + "properties": { + "name": { + "description": "Required. The name (project, location, operation id) of the operation to get. Specified in the format `projects/*/locations/*/operations/*`.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "name": "get_operation", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPOperation wraps the GKE Operation with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during the operation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operation": { + "description": "JSON string of the GKE Operation object. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Cancels a specific GKE operation.", + "inputSchema": { + "description": "MCPCancelOperationRequest cancels an operation.", + "properties": { + "name": { + "description": "Required. The name (project, location, operation id) of the operation to cancel. Specified in the format `projects/*/locations/*/operations/*`.", + "type": "string" + }, + "parent": { + "description": "Required. The parent cluster of the operation. Specified in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + } + }, + "required": [ + "name", + "parent" + ], + "type": "object" + }, + "name": "cancel_operation", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPCancelOperationResponse wraps errors for the CancelOperation RPC.", + "properties": { + "errors": { + "description": "Errors encountered during operation cancellation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Creates a node pool for a specific GKE cluster.", + "inputSchema": { + "description": "MCPCreateNodePoolRequest creates a node pool for a cluster.", + "properties": { + "nodePool": { + "description": "Required. The node pool to create represented as a string using JSON format. The full node pool object can be found at https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters.nodePools", + "type": "string" + }, + "parent": { + "description": "Required. The parent (project, location, cluster name) where the node pool will be created. Specified in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + } + }, + "required": [ + "parent", + "nodePool" + ], + "type": "object" + }, + "name": "create_node_pool", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPOperation wraps the GKE Operation with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during the operation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operation": { + "description": "JSON string of the GKE Operation object. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Lists the node pools for a specific GKE cluster.", + "inputSchema": { + "description": "MCPListNodePoolsRequest lists the node pool(s) for a cluster.", + "properties": { + "parent": { + "description": "Required. The parent (project, location, cluster name) where the node pools will be listed. Specified in the format `projects/*/locations/*/clusters/*`.", + "type": "string" + } + }, + "required": [ + "parent" + ], + "type": "object" + }, + "name": "list_node_pools", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for listing node pools in JSON format. The full node pool object can be found at https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters.nodePools", + "properties": { + "errors": { + "description": "Errors encountered during node pool listing.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "nodePools": { + "description": "A string representing the ListNodePoolsResponse object.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets the details of a specific node pool within a GKE cluster.", + "inputSchema": { + "description": "MCPGetNodePoolRequest retrieves a node pool for a cluster.", + "properties": { + "name": { + "description": "Required. The name (project, location, cluster, node pool id) of the node pool to get. Specified in the format `projects/*/locations/*/clusters/*/nodePools/*`.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "name": "get_node_pool", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for returned node pool object in JSON format. The full node pool object can be found at https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters.nodePools", + "properties": { + "errors": { + "description": "Errors encountered during node pool retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "nodePool": { + "description": "A string representing the NodePool object in JSON format.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Updates a specific node pool within a GKE cluster.", + "inputSchema": { + "description": "MCPUpdateNodePoolRequest updates a node pool for a cluster.", + "properties": { + "name": { + "description": "Required. The name (project, location, cluster, node pool) of the node pool to update. Specified in the format 'projects/*/locations/*/clusters/*/nodePools/*'.", + "type": "string" + }, + "update": { + "description": "Required. A [node pool update request](https://cloud.google.com/container-engine/reference/rest/v1/projects.locations.clusters.nodePools/update) represented as a string using JSON format.", + "type": "string" + } + }, + "required": [ + "name", + "update" + ], + "type": "object" + }, + "name": "update_node_pool", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "MCPOperation wraps the GKE Operation with an errors field.", + "properties": { + "errors": { + "description": "Errors encountered during the operation.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "operation": { + "description": "JSON string of the GKE Operation object. See: https://docs.cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.operations", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Gets logs from a Kubernetes container in a pod. This is similar to running `kubectl logs`.", + "inputSchema": { + "description": "KubeLogsRequest is the request message for retrieving Kubernetes logs.", + "properties": { + "allContainers": { + "description": "Optional. If true, retrieve logs from all containers in the pod.", + "type": "boolean" + }, + "container": { + "description": "Optional. The name of the container to retrieve logs from. If not specified, logs from the first container are returned.", + "type": "string" + }, + "name": { + "description": "Required. The name of the resource to retrieve logs from. This can be a pod name (e.g. \"my-pod\") or a type/name (e.g. \"deployment/my-deployment\"). If a type is not specified, \"pod\" is assumed.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, \"default\" is used.", + "type": "string" + }, + "parent": { + "description": "Required. The cluster to retrieve logs from. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "previous": { + "description": "Optional. If true, retrieve logs from the previous instantiation of the container.", + "type": "boolean" + }, + "since": { + "description": "Optional. Retrieve logs since this duration ago (e.g. \"1h\", \"10m\").", + "format": "google-duration", + "type": "string" + }, + "sinceTime": { + "description": "Optional. Retrieve logs since this time (RFC3339). e.g. \"2024-08-30T06:00:00Z\".", + "format": "date-time", + "type": "string" + }, + "tail": { + "description": "Optional. The number of lines from the end of the logs to show.", + "format": "int64", + "type": "string" + }, + "timestamps": { + "description": "Optional. If true, include timestamps in the log output.", + "type": "boolean" + } + }, + "required": [ + "parent", + "name" + ], + "type": "object" + }, + "name": "get_k8s_logs", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "KubeLogsResponse is the response message for retrieving Kubernetes logs.", + "properties": { + "errors": { + "description": "Errors encountered during log retrieval.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "logs": { + "description": "The logs from the resources.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Applies a Kubernetes manifest to a cluster using server-side apply. This is similar to running `kubectl apply --server-side`.", + "inputSchema": { + "description": "KubeApplyRequest is the request message for applying Kubernetes manifests.", + "properties": { + "dryRun": { + "description": "Optional. If true, run in dry-run mode.", + "type": "boolean" + }, + "forceConflicts": { + "description": "Optional. If true, force conflicts resolution when applying.", + "type": "boolean" + }, + "parent": { + "description": "Required. The cluster to apply the manifest to. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "yamlManifest": { + "description": "Required. The YAML manifest to apply.", + "type": "string" + } + }, + "required": [ + "parent", + "yamlManifest" + ], + "type": "object" + }, + "name": "apply_k8s_manifest", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "KubeApplyResponse is the response message for applying Kubernetes manifests.", + "properties": { + "errors": { + "description": "Errors encountered during apply. If this field is populated, some resources may not have been applied.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "result": { + "description": "Result of the apply operation, e.g., resources created/configured. This might be a summary string or structured data.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Deletes a Kubernetes resource from a cluster. This is similar to running `kubectl delete`.", + "inputSchema": { + "description": "Request for deleting Kubernetes resources.", + "properties": { + "cascade": { + "description": "Optional. The cascading deletion policy to use. If not specified, 'background' is used. Valid values are 'background', 'foreground', and 'orphan'.", + "type": "string" + }, + "dryRun": { + "description": "Optional. If true, run in dry-run mode.", + "type": "boolean" + }, + "name": { + "description": "Required. The name of the resource to delete.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, \"default\" is used.", + "type": "string" + }, + "parent": { + "description": "Required. The cluster, which owns this collection of resources. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of resource to delete. Kubernetes resource/kind name in singular form, lower case. e.g. \"pod\", \"deployment\", \"service\".", + "type": "string" + } + }, + "required": [ + "parent", + "resourceType", + "name" + ], + "type": "object" + }, + "name": "delete_k8s_resource", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for deleting Kubernetes resources.", + "properties": { + "errors": { + "description": "Errors encountered during deletion.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "result": { + "description": "Result of the delete operation.", + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Patches a Kubernetes resource. This is similar to running `kubectl patch`.", + "inputSchema": { + "description": "Request for patching a Kubernetes resource.", + "properties": { + "name": { + "description": "Required. The name of the resource to patch.", + "type": "string" + }, + "namespace": { + "description": "Optional. The namespace of the resource. If not specified, \"default\" is used.", + "type": "string" + }, + "parent": { + "description": "Required. The cluster to patch the resource in. Format: projects/{project}/locations/{location}/clusters/{cluster}", + "type": "string" + }, + "patch": { + "description": "Required. The patch to apply in JSON format.", + "type": "string" + }, + "resourceType": { + "description": "Required. The type of resource to patch. e.g. \"pods\", \"deployments\", \"services\".", + "type": "string" + } + }, + "required": [ + "parent", + "resourceType", + "name", + "patch" + ], + "type": "object" + }, + "name": "patch_k8s_resource", + "outputSchema": { + "$defs": { + "Status": { + "description": "The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).", + "properties": { + "code": { + "description": "The status code, which should be an enum value of google.rpc.Code.", + "format": "int32", + "type": "integer" + }, + "details": { + "description": "A list of messages that carry the error details. There is a common set of message types for APIs to use.", + "items": { + "additionalProperties": { + "description": "Properties of the object. Contains field @type with type URL." + }, + "type": "object" + }, + "type": "array" + }, + "message": { + "description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response for patching a Kubernetes resource.", + "properties": { + "errors": { + "description": "Errors encountered during patching.", + "items": { + "$ref": "#/$defs/Status" + }, + "type": "array" + }, + "result": { + "description": "The result of the patch operation.", + "type": "string" + } + }, + "type": "object" + } + } + ] +} diff --git a/google-gke-official/server/tools/index.ts b/google-gke-official/server/tools/index.ts new file mode 100644 index 00000000..bb02c05e --- /dev/null +++ b/google-gke-official/server/tools/index.ts @@ -0,0 +1,9 @@ +import { wrapBackendSnapshot } from "@decocms/mcps-shared/google-mcp"; +import { BACKEND_URL, TOOL_SNAPSHOT } from "../constants.ts"; +import { getGoogleAccessToken } from "../lib/env.ts"; +import type { Env } from "../../shared/deco.gen.ts"; + +export const tools = wrapBackendSnapshot(TOOL_SNAPSHOT.tools, { + backendUrl: BACKEND_URL, + getAccessToken: (env) => getGoogleAccessToken(env as Env), +}); diff --git a/google-gke-official/shared/deco.gen.ts b/google-gke-official/shared/deco.gen.ts new file mode 100644 index 00000000..c535e482 --- /dev/null +++ b/google-gke-official/shared/deco.gen.ts @@ -0,0 +1,7 @@ +import { type DefaultEnv } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { z } from "zod"; + +export const StateSchema = z.object({}); + +export type Env = DefaultEnv; diff --git a/google-gke-official/tsconfig.json b/google-gke-official/tsconfig.json new file mode 100644 index 00000000..f715197c --- /dev/null +++ b/google-gke-official/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "ES2024", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowJs": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["server", "shared"] +} diff --git a/google-maps-official/README.md b/google-maps-official/README.md index c2dd7990..8632a883 100644 --- a/google-maps-official/README.md +++ b/google-maps-official/README.md @@ -1,6 +1,14 @@ # Google Maps MCP Server Official -This is the **official Google Maps MCP Server**, provided directly by the Google Maps team for integration with Google Maps Platform. +Thin wrapper around Google's official Maps MCP server (`mapstools.googleapis.com/mcp`). The wrapper holds the Google OAuth client credentials server-side and proxies JSON-RPC `tools/call` to the upstream — Google's MCP doesn't support Dynamic Client Registration, so this layer is needed to integrate it into mesh. + +See [`TOOLS.md`](./TOOLS.md) for the catalog (auto-generated via `bun run generate-tools`). Refresh after Google updates the upstream tools/list. + +The OAuth + proxy plumbing lives in `@decocms/mcps-shared/google-mcp` — same code path used by `google-workspace` and the other `google-*-official` wrappers. + +--- + +The upstream **Google Maps MCP Server** is provided directly by the Google Maps team for integration with Google Maps Platform. ## About Google Maps diff --git a/google-maps-official/TOOLS.md b/google-maps-official/TOOLS.md new file mode 100644 index 00000000..9e193f0b --- /dev/null +++ b/google-maps-official/TOOLS.md @@ -0,0 +1,15 @@ +# Google Maps — Tool Catalog + +_Auto-generated by `bun run generate-tools`. Do not edit by hand._ + +3 tools across 1 backend, gated behind 1 OAuth scope. + +**Endpoint:** `https://mapstools.googleapis.com/mcp` + +**Scopes:** +- `https://www.googleapis.com/auth/maps-platform.mapstools` + +**Tools (3):** +- `search_places` +- `lookup_weather` — Retrieves comprehensive weather data including current conditions, hourly, and daily forecasts. +- `compute_routes` — Computes a travel route between a specified origin and destination. **Supported Travel Modes:** DRIVE (default), WALK. diff --git a/google-maps-official/app.json b/google-maps-official/app.json index d14e3475..bf5ab91f 100644 --- a/google-maps-official/app.json +++ b/google-maps-official/app.json @@ -4,7 +4,7 @@ "friendlyName": "Google Maps", "connection": { "type": "HTTP", - "url": "https://mapstools.googleapis.com/mcp" + "url": "https://sites-google-maps-official.decocache.com/mcp" }, "description": "Access Google Maps Platform APIs through natural language. Get directions, geocoding, places data, and mapping services with AI assistance.", "icon": "https://www.gstatic.com/images/branding/product/2x/maps_64dp.png", @@ -12,7 +12,7 @@ "metadata": { "categories": ["Mapping"], "official": true, - "mesh_unlisted": true, + "mesh_unlisted": false, "tags": ["maps", "geocoding", "directions", "places", "location", "google-maps", "geospatial", "navigation", "gis"], "short_description": "Access Google Maps Platform APIs through natural language", "mesh_description": "Google Maps Platform provides comprehensive mapping and location services used by millions of applications worldwide. This official MCP gives you natural language access to Google Maps APIs including geocoding for converting addresses to coordinates and reverse geocoding for coordinates to addresses. Get optimal route directions with multiple transportation modes (driving, walking, bicycling, transit) considering real-time traffic conditions, toll roads, and route alternatives. Search and discover places with detailed information including business hours, ratings, reviews, photos, and contact details. Calculate accurate distances and travel times between multiple locations using the Distance Matrix API. Optimize multi-stop routes with the Directions API for efficient delivery and logistics planning. Access detailed place information with the Places API including nearby search, text search, and place details. Use the Geolocation API to determine device location based on cell towers and WiFi access points. Implement time zone services to convert coordinates to time zones and calculate local times. Create custom maps with markers, polylines, polygons, and info windows. Access Street View imagery programmatically for location verification and virtual tours. Utilize Elevation API for topographic data and terrain information. Perfect for location-based applications, delivery services, real estate platforms, and travel apps." diff --git a/google-maps-official/package.json b/google-maps-official/package.json new file mode 100644 index 00000000..2b13303f --- /dev/null +++ b/google-maps-official/package.json @@ -0,0 +1,32 @@ +{ + "name": "google-maps-official", + "version": "1.0.0", + "description": "Wrapper around Google's official Maps MCP server (mapstools.googleapis.com/mcp)", + "private": true, + "type": "module", + "scripts": { + "dev": "bun run --hot server/main.ts", + "build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js", + "build": "bun run build:server", + "publish": "cat app.json | deco registry publish -w /shared/deco -y", + "check": "tsc --noEmit", + "generate-tools": "bun run server/scripts/generate-tools.ts" + }, + "exports": { + "./tools": "./server/tools/index.ts", + "./constants": "./server/constants.ts" + }, + "dependencies": { + "@decocms/runtime": "^1.2.6", + "zod": "^4.0.0" + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/google-maps-official/server/constants.ts b/google-maps-official/server/constants.ts new file mode 100644 index 00000000..7c23dd95 --- /dev/null +++ b/google-maps-official/server/constants.ts @@ -0,0 +1,15 @@ +import snapshot from "./tools/generated/maps.json" with { type: "json" }; +import type { BackendToolDefinition } from "@decocms/mcps-shared/google-mcp"; + +export const BACKEND_URL = "https://mapstools.googleapis.com/mcp"; + +export interface BackendSnapshot { + service: string; + url: string; + scopes: string[]; + tools: BackendToolDefinition[]; +} + +export const TOOL_SNAPSHOT = snapshot as BackendSnapshot; + +export const SCOPES: string[] = TOOL_SNAPSHOT.scopes; diff --git a/google-maps-official/server/lib/env.ts b/google-maps-official/server/lib/env.ts new file mode 100644 index 00000000..d3f4c238 --- /dev/null +++ b/google-maps-official/server/lib/env.ts @@ -0,0 +1,9 @@ +import type { Env } from "../../shared/deco.gen.ts"; + +export const getGoogleAccessToken = (env: Env): string => { + const authorization = env.MESH_REQUEST_CONTEXT?.authorization; + if (!authorization) { + throw new Error("Not authenticated. Please authorize Google Maps first."); + } + return authorization; +}; diff --git a/google-maps-official/server/main.ts b/google-maps-official/server/main.ts new file mode 100644 index 00000000..024c1b75 --- /dev/null +++ b/google-maps-official/server/main.ts @@ -0,0 +1,21 @@ +import { withRuntime } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { serve } from "@decocms/mcps-shared/serve"; +import { createGoogleOAuth } from "@decocms/mcps-shared/google-oauth"; + +import { tools } from "./tools/index.ts"; +import { SCOPES } from "./constants.ts"; +import { type Env, StateSchema } from "../shared/deco.gen.ts"; + +export type { Env }; + +const runtime = withRuntime({ + configuration: { + scopes: [], + state: StateSchema, + }, + tools: (env: Env) => tools.map((createTool) => createTool(env)), + oauth: createGoogleOAuth({ scopes: SCOPES }), +}); + +serve(runtime.fetch); diff --git a/google-maps-official/server/scripts/generate-tools.ts b/google-maps-official/server/scripts/generate-tools.ts new file mode 100644 index 00000000..623807be --- /dev/null +++ b/google-maps-official/server/scripts/generate-tools.ts @@ -0,0 +1,16 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { generateSnapshots } from "@decocms/mcps-shared/google-mcp/generate-snapshot"; +import { BACKEND_URL } from "../constants.ts"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const OUT_DIR = join(HERE, "..", "tools", "generated"); +const PACKAGE_ROOT = join(HERE, "..", ".."); +const TOOLS_MD_PATH = join(PACKAGE_ROOT, "TOOLS.md"); + +await generateSnapshots({ + title: "Google Maps — Tool Catalog", + outDir: OUT_DIR, + toolsMarkdownPath: TOOLS_MD_PATH, + backends: [{ service: "maps", url: BACKEND_URL }], +}); diff --git a/google-maps-official/server/tools/generated/_index.json b/google-maps-official/server/tools/generated/_index.json new file mode 100644 index 00000000..246ac951 --- /dev/null +++ b/google-maps-official/server/tools/generated/_index.json @@ -0,0 +1,12 @@ +{ + "maps": { + "scopes": [ + "https://www.googleapis.com/auth/maps-platform.mapstools" + ], + "toolNames": [ + "search_places", + "lookup_weather", + "compute_routes" + ] + } +} diff --git a/google-maps-official/server/tools/generated/maps.json b/google-maps-official/server/tools/generated/maps.json new file mode 100644 index 00000000..f98a48b3 --- /dev/null +++ b/google-maps-official/server/tools/generated/maps.json @@ -0,0 +1,957 @@ +{ + "service": "maps", + "url": "https://mapstools.googleapis.com/mcp", + "scopes": [ + "https://www.googleapis.com/auth/maps-platform.mapstools" + ], + "tools": [ + { + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "\nCall this tool when the user's request is to find places, businesses, addresses, locations, points of interest, or any other Google Maps related search.\n\n**Input Requirements (CRITICAL):**\n\n1. **`text_query` (string - MANDATORY):** The primary search query. This must clearly define what the user is looking for.\n\n * **Examples:** `'restaurants in New York'`, `'coffee shops near Golden Gate Park'`, `'SF MoMA'`, `'1600 Amphitheatre Pkwy, Mountain View, CA, USA'`, `'pets friendly parks in Manhattan, New York'`, `'date night restaurants in Chicago'`, `'accessible public libraries in Los Angeles'`.\n\n * **For specific place details:** Include the requested attribute (e.g., `'Google Store Mountain View opening hours'`, `'SF MoMa phone number'`, `'Shoreline Park Mountain View address'`).\n\n2. **`location_bias` (object - OPTIONAL):** Use this to prioritize results near a specific geographic area.\n * **Format:** `{\"location_bias\": {\"circle\": {\"center\": {\"latitude\": [value], \"longitude\": [value]}, \"radius_meters\": [value (optional)]}}}`\n\n * **Usage:**\n * **To bias to a 5km radius:** `{\"location_bias\": {\"circle\": {\"center\": {\"latitude\": 34.052235, \"longitude\": -118.243683}, \"radius_meters\": 5000}}}`\n * **To bias strongly to the center point:** `{\"location_bias\": {\"circle\": {\"center\": {\"latitude\": 34.052235, \"longitude\": -118.243683}}}}` (omitting `radius_meters`).\n\n3. **`language_code` (string - OPTIONAL):** The language to show the search results summary in.\n * **Format:** A two-letter language code (ISO 639-1), optionally followed by an underscore and a two-letter country code (ISO 3166-1 alpha-2), e.g., `en`, `ja`, `en_US`, `zh_CN`, `es_MX`. If the language code is not provided, the results will be in English.\n\n4. **`region_code` (string - OPTIONAL):** The Unicode CLDR region code of the user. This parameter is used to display the place details, like region-specific place name, if available. The parameter canaffect results based on applicable law.\n * **Format:** A two-letter country code (ISO 3166-1 alpha-2), e.g., `US`, `CA`.\n\n**Instructions for Tool Call:**\n\n* Location Information (CRITICAL): The search must contain sufficient location information. If the location is ambiguous (e.g., just \"pizza places\"), *you must* specify it in the `text_query` (e.g., \"pizza places in New York\") or use the `location_bias` parameter. Include city, state/province, and region/country name if needed for disambiguation.\n\n* Always provide the most specific and contextually rich `text_query` possible.\n\n* Only use `location_bias` if coordinates are explicitly provided or if inferring a location from a user's known context is appropriate *and* necessary for better results.\n\n* The grounded output must be attributed to the source using the information from the `attribution` field when available.\n", + "inputSchema": { + "$defs": { + "Circle": { + "description": "A circle defined by center point and radius.", + "properties": { + "center": { + "$ref": "#/$defs/LatLng", + "description": "Required. The center point of the circle." + }, + "radiusMeters": { + "description": "The radius of the circle in meters. The radius must be within 50,000 meters.", + "format": "double", + "type": "number" + } + }, + "required": [ + "center" + ], + "type": "object" + }, + "LatLng": { + "description": "An object that represents a latitude/longitude pair. This is expressed as a pair of doubles to represent degrees latitude and degrees longitude. Unless specified otherwise, this object must conform to the WGS84 standard . Values must be within normalized ranges.", + "properties": { + "latitude": { + "description": "The latitude in degrees. It must be in the range [-90.0, +90.0].", + "format": "double", + "type": "number" + }, + "longitude": { + "description": "The longitude in degrees. It must be in the range [-180.0, +180.0].", + "format": "double", + "type": "number" + } + }, + "type": "object" + }, + "LocationBias": { + "description": "The region to bias the search results to. Places outside of this region may still be returned.", + "properties": { + "circle": { + "$ref": "#/$defs/Circle", + "description": "Optional. A circle defined by center point and radius. The `radius_meters` is optional. If not set, the results will be biased towards the center point." + } + }, + "type": "object" + } + }, + "description": "Request message for SearchText.", + "properties": { + "languageCode": { + "description": "Optional. The language to request that the summary is returned in. If the language code is unspecified or unrecognized, the summary with a preference for English will be returned. For example, \"en\" for English. Current list of supported languages: https://developers.google.com/maps/faq#languagesupport.", + "type": "string" + }, + "locationBias": { + "$ref": "#/$defs/LocationBias", + "description": "An optional region to bias the search results to. If an explicit location is in `text_query`, it will be used to bias the search results instead of this field." + }, + "regionCode": { + "description": "Optional. The Unicode country/region code (CLDR) of the location where the request is coming from. This parameter is used to display the place details, like region-specific place name, if available. The parameter can affect results based on applicable law. For example, \"US\" for United States. For more information, see https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html. Note that 3-digit region codes are not currently supported.", + "type": "string" + }, + "textQuery": { + "description": "Required. The text query.", + "type": "string" + } + }, + "required": [ + "textQuery" + ], + "type": "object" + }, + "name": "search_places", + "outputSchema": { + "$defs": { + "Attribution": { + "description": "Required attribution to show with maps content.", + "properties": { + "title": { + "description": "The title to display for the attribution.", + "type": "string" + }, + "url": { + "description": "The URL to link to for the attribution.", + "type": "string" + } + }, + "type": "object" + }, + "GoogleMapsLinks": { + "description": "Links to trigger different Google Maps actions.", + "properties": { + "directionsUrl": { + "description": "A link to show the directions to the place. The link only populates the destination location and uses the default travel mode `DRIVE`.", + "type": "string" + }, + "photosUrl": { + "description": "A link to show photos of this place on Google Maps.", + "type": "string" + }, + "placeUrl": { + "description": "A link to show this place.", + "type": "string" + }, + "reviewsUrl": { + "description": "A link to show reviews of this place on Google Maps.", + "type": "string" + }, + "writeAReviewUrl": { + "description": "A link to write a review for this place on Google Maps.", + "type": "string" + } + }, + "type": "object" + }, + "LatLng": { + "description": "An object that represents a latitude/longitude pair. This is expressed as a pair of doubles to represent degrees latitude and degrees longitude. Unless specified otherwise, this object must conform to the WGS84 standard . Values must be within normalized ranges.", + "properties": { + "latitude": { + "description": "The latitude in degrees. It must be in the range [-90.0, +90.0].", + "format": "double", + "type": "number" + }, + "longitude": { + "description": "The longitude in degrees. It must be in the range [-180.0, +180.0].", + "format": "double", + "type": "number" + } + }, + "type": "object" + }, + "PlaceView": { + "description": "A view of a place.", + "properties": { + "attribution": { + "$ref": "#/$defs/Attribution", + "description": "Required attribution to show with the place." + }, + "googleMapsLinks": { + "$ref": "#/$defs/GoogleMapsLinks", + "description": "Links to trigger different Google Maps actions." + }, + "id": { + "description": "The place ID of the underlying place.", + "type": "string" + }, + "location": { + "$ref": "#/$defs/LatLng", + "description": "The position of this place." + }, + "place": { + "description": "The resource name of the underlying place, in the format of \"places/{id}\".", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Response message for SearchText.", + "properties": { + "places": { + "description": "Output only. The list of places that are mentioned in the summary.", + "items": { + "$ref": "#/$defs/PlaceView" + }, + "readOnly": true, + "type": "array" + }, + "summary": { + "description": "Output only. A natural language summary of the search results. The summary may contain zero-based citations like \"[0]\", \"[1]\", \"[2]\" etc. These citations map to the corresponding places in the `places` field.", + "readOnly": true, + "type": "string" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Retrieves comprehensive weather data including current conditions, hourly, and daily forecasts.\n\n**Specific Data Available:** Temperature (Current, Feels Like, Max/Min, Heat Index), Wind (Speed, Gusts, Direction), Celestial Events (Sunrise/Sunset, Moon Phase), Precipitation (Type, Probability, Quantity/QPF), Atmospheric Conditions (UV Index, Humidity, Cloud Cover, Thunderstorm Probability), and Geocoded Location Address.\n\n**Location & Location Rules (CRITICAL):**\n\nThe location for which weather data is requested is specified using the `location` field.\nThis field is a 'oneof' structure, meaning you MUST provide a value for ONLY ONE\nof the three location sub-fields below to ensure an accurate weather data lookup.\n\n1. Geographic Coordinates (lat_lng)\n * Use it when you are provided with exact lat/lng coordinates.\n * Example:\n {\"location\": {\"lat_lng\": {\"latitude\": 34.0522, \"longitude\": -118.2437}}} // Los Angeles\n\n2. Place ID (place_id)\n * An unambiguous string identifier (Google Maps Place ID).\n * The place_id can be fetched from the search_places tool.\n * Example:\n {\"location\": {\"place_id\": \"ChIJLU7jZClu5kcR4PcOOO6p3I0\"}} // Eiffel Tower\n\n3. Address String (address)\n * A free-form string that requires specificity for geocoding.\n * City & Region: Always include region/country (e.g., \"London, UK\", not \"London\").\n * Street Address: Provide the full address (e.g., \"1600 Pennsylvania Ave NW, Washington, DC\").\n * Postal/Zip Codes: MUST be accompanied by a country name (e.g., \"90210, USA\", NOT \"90210\").\n * Example:\n {\"location\": {\"address\": \"1600 Pennsylvania Ave NW, Washington, DC\"}}\n\n**Usage Modes:**\n\n* **Current Weather:** Provide `location` only. Do not specify `date` and `hour`.\n\n* **Hourly Forecast:** Provide `location`, `date`, and `hour` (0-23). Use for specific times (e.g., \"at 5 PM\") or terms like \"next few hours\" or \"later today\". If the user specifies minute, round down to the nearest hour. Hourly forecast beyond 120 hours from now is not supported. Historical hourly weather is supported up to 24 hours in the past.\n\n* **Daily Forecast:** Provide `location` and `date`. Do not specify `hour`. Use for general day requests (e.g., \"weather for tomorrow\", \"weather on Friday\", \"weather on 12/25\"). If today's date is not in the context, you should clarify it with the user. Daily forecast beyond 10 days including today is not supported. Historical weather is not supported.\n\n**Parameter Constraints:**\n\n* **Timezones:** All `date` and `hour` inputs must be relative to the **location's local time zone**, not the user's time zone.\n* **Date Format:** Inputs must be separated into `{year, month, day}` integers.\n* **Units:** Defaults to `METRIC`. Set `units_system` to `IMPERIAL` for Fahrenheit/Miles if the user implies US standards or explicitly requests it.\n\n* The grounded output must be attributed to the source using the information from the `attribution` field when available.\n", + "inputSchema": { + "$defs": { + "Date": { + "description": "Represents a whole or partial calendar date, such as a birthday. The time of day and time zone are either specified elsewhere or are insignificant. The date is relative to the Gregorian Calendar. This can represent one of the following: * A full date, with non-zero year, month, and day values. * A month and day, with a zero year (for example, an anniversary). * A year on its own, with a zero month and a zero day. * A year and month, with a zero day (for example, a credit card expiration date). Related types: * google.type.TimeOfDay * google.type.DateTime * google.protobuf.Timestamp", + "properties": { + "day": { + "description": "Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 to specify a year by itself or a year and month where the day isn't significant.", + "format": "int32", + "type": "integer" + }, + "month": { + "description": "Month of a year. Must be from 1 to 12, or 0 to specify a year without a month and day.", + "format": "int32", + "type": "integer" + }, + "year": { + "description": "Year of the date. Must be from 1 to 9999, or 0 to specify a date without a year.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "LatLng": { + "description": "An object that represents a latitude/longitude pair. This is expressed as a pair of doubles to represent degrees latitude and degrees longitude. Unless specified otherwise, this object must conform to the WGS84 standard . Values must be within normalized ranges.", + "properties": { + "latitude": { + "description": "The latitude in degrees. It must be in the range [-90.0, +90.0].", + "format": "double", + "type": "number" + }, + "longitude": { + "description": "The longitude in degrees. It must be in the range [-180.0, +180.0].", + "format": "double", + "type": "number" + } + }, + "type": "object" + }, + "Location": { + "description": "Represents a location for the weather request.", + "properties": { + "address": { + "description": "Human readable address or a plus code. See https://plus.codes for details.", + "type": "string" + }, + "latLng": { + "$ref": "#/$defs/LatLng", + "description": "A point specified using geographic coordinates." + }, + "placeId": { + "description": "The Place ID associated with the location .", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "Request for the LookupWeather method - represents the weather conditions at the requested location.", + "properties": { + "date": { + "$ref": "#/$defs/Date", + "description": "Optional. The date of the required weather information. Note: This date is relative to the local timezone of the location specified in the location field. The date must be between 24 hours in the past and the next 10 days." + }, + "hour": { + "description": "Optional. The hour of the requested weather information, in 24-hour format (0-23). This value is relative to the local timezone of the location specified in the location field. Hourly forecast beyond 120 hours from now is not supported. Historical hourly weather is supported up to 24 hours in the past.", + "format": "int32", + "type": "integer" + }, + "location": { + "$ref": "#/$defs/Location", + "description": "Required. The location to get the weather conditions for." + }, + "unitsSystem": { + "description": "Optional. The units system to use for the returned weather conditions. If not provided, the returned weather conditions will be in the metric system (default = METRIC).", + "enum": [ + "UNITS_SYSTEM_UNSPECIFIED", + "IMPERIAL", + "METRIC" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The units system is unspecified.", + "The imperial units system (e.g. Fahrenheit, miles, etc).", + "The metric units system (e.g. Celsius, kilometers, etc)." + ] + } + }, + "required": [ + "location" + ], + "type": "object" + }, + "name": "lookup_weather", + "outputSchema": { + "$defs": { + "AirPressure": { + "description": "Represents the atmospheric air pressure conditions.", + "properties": { + "meanSeaLevelMillibars": { + "description": "The mean sea level air pressure in millibars.", + "format": "float", + "type": "number" + } + }, + "type": "object" + }, + "Attribution": { + "description": "Required attribution to show with maps content.", + "properties": { + "title": { + "description": "The title to display for the attribution.", + "type": "string" + }, + "url": { + "description": "The URL to link to for the attribution.", + "type": "string" + } + }, + "type": "object" + }, + "LatLng": { + "description": "An object that represents a latitude/longitude pair. This is expressed as a pair of doubles to represent degrees latitude and degrees longitude. Unless specified otherwise, this object must conform to the WGS84 standard . Values must be within normalized ranges.", + "properties": { + "latitude": { + "description": "The latitude in degrees. It must be in the range [-90.0, +90.0].", + "format": "double", + "type": "number" + }, + "longitude": { + "description": "The longitude in degrees. It must be in the range [-180.0, +180.0].", + "format": "double", + "type": "number" + } + }, + "type": "object" + }, + "LocalizedText": { + "description": "Localized variant of a text in a particular language.", + "properties": { + "languageCode": { + "description": "The text's BCP-47 language code, such as \"en-US\" or \"sr-Latn\". For more information, see http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.", + "type": "string" + }, + "text": { + "description": "Localized string in the language corresponding to language_code below.", + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "description": "Represents a location for the weather request.", + "properties": { + "address": { + "description": "Human readable address or a plus code. See https://plus.codes for details.", + "type": "string" + }, + "latLng": { + "$ref": "#/$defs/LatLng", + "description": "A point specified using geographic coordinates." + }, + "placeId": { + "description": "The Place ID associated with the location .", + "type": "string" + } + }, + "type": "object" + }, + "MoonEvents": { + "description": "Represents the events related to the moon (e.g. moonrise, moonset).", + "properties": { + "moonPhase": { + "description": "The moon phase (a.k.a. lunar phase).", + "enum": [ + "MOON_PHASE_UNSPECIFIED", + "NEW_MOON", + "WAXING_CRESCENT", + "FIRST_QUARTER", + "WAXING_GIBBOUS", + "FULL_MOON", + "WANING_GIBBOUS", + "LAST_QUARTER", + "WANING_CRESCENT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified moon phase.", + "The moon is not illuminated by the sun.", + "The moon is lit by 0%-50% on its right side in the northern hemisphere 🌒 and on its left side in the southern hemisphere 🌘.", + "The moon is lit by 50.1% on its right side in the northern hemisphere 🌓 and on its left side in the southern hemisphere 🌗.", + "The moon is lit by 50%-100% on its right side in the northern hemisphere 🌔 and on its left side in the southern hemisphere 🌖.", + "The moon is fully illuminated.", + "The moon is lit by 50%-100% on its left side in the northern hemisphere 🌖 and on its right side in the southern hemisphere 🌔.", + "The moon is lit by 50.1% on its left side in the northern hemisphere 🌗 and on its right side in the southern hemisphere 🌓.", + "The moon is lit by 0%-50% on its left side in the northern hemisphere 🌘 and on its right side in the southern hemisphere 🌒." + ] + }, + "moonriseTimes": { + "description": "The time when the upper limb of the moon appears above the horizon (see https://en.wikipedia.org/wiki/Moonrise_and_moonset). NOTE: For most cases, there'll be a single moon rise time per day. In other cases, the list might be empty (e.g. when the moon rises after next day midnight). However, in unique cases (e.g. in polar regions), the list may contain more than one value. In these cases, the values are sorted in ascending order.", + "items": { + "format": "date-time", + "type": "string" + }, + "type": "array" + }, + "moonsetTimes": { + "description": "The time when the upper limb of the moon disappears below the horizon (see https://en.wikipedia.org/wiki/Moonrise_and_moonset). NOTE: For most cases, there'll be a single moon set time per day. In other cases, the list might be empty (e.g. when the moon sets after next day midnight). However, in unique cases (e.g. in polar regions), the list may contain more than one value. In these cases, the values are sorted in ascending order.", + "items": { + "format": "date-time", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Precipitation": { + "description": "Represents a set of precipitation values at a given location.", + "properties": { + "probability": { + "$ref": "#/$defs/PrecipitationProbability", + "description": "The probability of precipitation (values from 0 to 100)." + }, + "qpf": { + "$ref": "#/$defs/QuantitativePrecipitationForecast", + "description": "The amount of precipitation rain, measured as liquid water equivalent, that has accumulated over a period of time. Note: QPF is an abbreviation for Quantitative Precipitation Forecast (please see the QuantitativePrecipitationForecast definition for more details)." + }, + "snowQpf": { + "$ref": "#/$defs/QuantitativePrecipitationForecast", + "description": "The amount of snow, measured as liquid water equivalent, that has accumulated over a period of time. Note: QPF is an abbreviation for Quantitative Precipitation Forecast (please see the QuantitativePrecipitationForecast definition for more details)." + } + }, + "type": "object" + }, + "PrecipitationProbability": { + "description": "Represents the probability of precipitation at a given location.", + "properties": { + "percent": { + "description": "A percentage from 0 to 100 that indicates the chances of precipitation.", + "format": "int32", + "type": "integer" + }, + "type": { + "description": "A code that indicates the type of precipitation.", + "enum": [ + "PRECIPITATION_TYPE_UNSPECIFIED", + "NONE", + "SNOW", + "RAIN", + "LIGHT_RAIN", + "HEAVY_RAIN", + "RAIN_AND_SNOW", + "SLEET", + "FREEZING_RAIN" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified precipitation type.", + "No precipitation.", + "Snow precipitation.", + "Rain precipitation.", + "Light rain precipitation.", + "Heavy rain precipitation.", + "Both rain and snow precipitations.", + "Sleet precipitation.", + "Freezing rain precipitation." + ] + } + }, + "type": "object" + }, + "QuantitativePrecipitationForecast": { + "description": "Represents the expected amount of melted precipitation accumulated over a specified time period over a specified area (reference: https://en.wikipedia.org/wiki/Quantitative_precipitation_forecast) - usually abbreviated QPF for short.", + "properties": { + "quantity": { + "description": "The amount of precipitation, measured as liquid water equivalent, that has accumulated over a period of time.", + "format": "float", + "type": "number" + }, + "unit": { + "description": "The code of the unit used to measure the amount of accumulated precipitation.", + "enum": [ + "UNIT_UNSPECIFIED", + "MILLIMETERS", + "INCHES" + ], + "type": "string", + "x-google-enum-descriptions": [ + "Unspecified precipitation unit.", + "The amount of precipitation is measured in millimeters.", + "The amount of precipitation is measured in inches." + ] + } + }, + "type": "object" + }, + "SunEvents": { + "description": "Represents the events related to the sun (e.g. sunrise, sunset).", + "properties": { + "sunriseTime": { + "description": "The time when the sun rises. NOTE: In some unique cases (e.g. north of the artic circle) there may be no sunrise time for a day. In these cases, this field will be unset.", + "format": "date-time", + "type": "string" + }, + "sunsetTime": { + "description": "The time when the sun sets. NOTE: In some unique cases (e.g. north of the artic circle) there may be no sunset time for a day. In these cases, this field will be unset.", + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "Temperature": { + "description": "Represents a temperature value.", + "properties": { + "degrees": { + "description": "The temperature value (in degrees) in the specified unit.", + "format": "float", + "type": "number" + }, + "unit": { + "description": "The code for the unit used to measure the temperature value.", + "enum": [ + "TEMPERATURE_UNIT_UNSPECIFIED", + "CELSIUS", + "FAHRENHEIT" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The temperature unit is unspecified.", + "The temperature is measured in Celsius.", + "The temperature is measured in Fahrenheit." + ] + } + }, + "type": "object" + }, + "WeatherCondition": { + "description": "Represents a weather condition for a given location at a given period of time. Disclaimer: Weather icons and condition codes are subject to change. Google may introduce new codes and icons or update existing ones as needed. We encourage you to refer to this documentation regularly for the most up-to-date information.", + "properties": { + "description": { + "$ref": "#/$defs/LocalizedText", + "description": "The textual description for this weather condition (localized)." + }, + "iconBaseUri": { + "description": "The base URI for the icon not including the file type extension. To display the icon, append a theme if desired and the file type extension (`.png` or `.svg`) to this URI. By default, the icon is light themed, but `_dark` can be appended for dark mode. For example: \"https://maps.gstatic.com/weather/v1/dust.svg\" or \"https://maps.gstatic.com/weather/v1/dust_dark.svg\", where `icon_base_uri` is \"https://maps.gstatic.com/weather/v1/dust\".", + "type": "string" + }, + "type": { + "description": "The type of weather condition.", + "enum": [ + "TYPE_UNSPECIFIED", + "CLEAR", + "MOSTLY_CLEAR", + "PARTLY_CLOUDY", + "MOSTLY_CLOUDY", + "CLOUDY", + "WINDY", + "WIND_AND_RAIN", + "LIGHT_RAIN_SHOWERS", + "CHANCE_OF_SHOWERS", + "SCATTERED_SHOWERS", + "RAIN_SHOWERS", + "HEAVY_RAIN_SHOWERS", + "LIGHT_TO_MODERATE_RAIN", + "MODERATE_TO_HEAVY_RAIN", + "RAIN", + "LIGHT_RAIN", + "HEAVY_RAIN", + "RAIN_PERIODICALLY_HEAVY", + "LIGHT_SNOW_SHOWERS", + "CHANCE_OF_SNOW_SHOWERS", + "SCATTERED_SNOW_SHOWERS", + "SNOW_SHOWERS", + "HEAVY_SNOW_SHOWERS", + "LIGHT_TO_MODERATE_SNOW", + "MODERATE_TO_HEAVY_SNOW", + "SNOW", + "LIGHT_SNOW", + "HEAVY_SNOW", + "SNOWSTORM", + "SNOW_PERIODICALLY_HEAVY", + "HEAVY_SNOW_STORM", + "BLOWING_SNOW", + "RAIN_AND_SNOW", + "HAIL", + "HAIL_SHOWERS", + "THUNDERSTORM", + "THUNDERSHOWER", + "LIGHT_THUNDERSTORM_RAIN", + "SCATTERED_THUNDERSTORMS", + "HEAVY_THUNDERSTORM" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The weather condition is unspecified.", + "No clouds.", + "Periodic clouds.", + "Party cloudy (some clouds).", + "Mostly cloudy (more clouds than sun).", + "Cloudy (all clouds, no sun).", + "High wind.", + "High wind with precipitation.", + "Light intermittent rain.", + "Chance of intermittent rain.", + "Intermittent rain.", + "Showers are considered to be rainfall that has a shorter duration than rain, and is characterized by suddenness in terms of start and stop times, and rapid changes in intensity.", + "Intense showers.", + "Rain (light to moderate in quantity).", + "Rain (moderate to heavy in quantity).", + "Moderate rain.", + "Light rain.", + "Heavy rain.", + "Rain periodically heavy.", + "Light snow that is falling at varying intensities for brief periods of time.", + "Chance of snow showers.", + "Snow that is falling at varying intensities for brief periods of time.", + "Snow showers.", + "Heavy snow showers.", + "Light to moderate snow.", + "Moderate to heavy snow.", + "Moderate snow.", + "Light snow.", + "Heavy snow.", + "Snow with possible thunder and lightning.", + "Snow, at times heavy.", + "Heavy snow with possible thunder and lightning.", + "Snow with intense wind.", + "Rain and snow mix.", + "Hail.", + "Hail that is falling at varying intensities for brief periods of time.", + "Thunderstorm.", + "A shower of rain accompanied by thunder and lightning.", + "Light thunderstorm rain.", + "Thunderstorms that has rain in various intensities for brief periods of time.", + "Heavy thunderstorm." + ] + } + }, + "type": "object" + }, + "Wind": { + "description": "Represents a set of wind properties.", + "properties": { + "direction": { + "$ref": "#/$defs/WindDirection", + "description": "The direction of the wind, the angle it is coming from." + }, + "gust": { + "$ref": "#/$defs/WindSpeed", + "description": "The wind gust (sudden increase in the wind speed)." + }, + "speed": { + "$ref": "#/$defs/WindSpeed", + "description": "The speed of the wind." + } + }, + "type": "object" + }, + "WindDirection": { + "description": "Represents the direction from which the wind originates.", + "properties": { + "cardinal": { + "description": "The code that represents the cardinal direction from which the wind is blowing.", + "enum": [ + "CARDINAL_DIRECTION_UNSPECIFIED", + "NORTH", + "NORTH_NORTHEAST", + "NORTHEAST", + "EAST_NORTHEAST", + "EAST", + "EAST_SOUTHEAST", + "SOUTHEAST", + "SOUTH_SOUTHEAST", + "SOUTH", + "SOUTH_SOUTHWEST", + "SOUTHWEST", + "WEST_SOUTHWEST", + "WEST", + "WEST_NORTHWEST", + "NORTHWEST", + "NORTH_NORTHWEST" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The cardinal direction is unspecified.", + "The north cardinal direction.", + "The north-northeast secondary intercardinal direction.", + "The northeast intercardinal direction.", + "The east-northeast secondary intercardinal direction.", + "The east cardinal direction.", + "The east-southeast secondary intercardinal direction.", + "The southeast intercardinal direction.", + "The south-southeast secondary intercardinal direction.", + "The south cardinal direction.", + "The south-southwest secondary intercardinal direction.", + "The southwest intercardinal direction.", + "The west-southwest secondary intercardinal direction.", + "The west cardinal direction.", + "The west-northwest secondary intercardinal direction.", + "The northwest intercardinal direction.", + "The north-northwest secondary intercardinal direction." + ] + }, + "degrees": { + "description": "The direction of the wind in degrees (values from 0 to 360).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "WindSpeed": { + "description": "Represents the speed of the wind.", + "properties": { + "unit": { + "description": "The code that represents the unit used to measure the wind speed.", + "enum": [ + "SPEED_UNIT_UNSPECIFIED", + "KILOMETERS_PER_HOUR", + "MILES_PER_HOUR" + ], + "type": "string", + "x-google-enum-descriptions": [ + "The speed unit is unspecified.", + "The speed is measured in kilometers per hour.", + "The speed is measured in miles per hour." + ] + }, + "value": { + "description": "The value of the wind speed.", + "format": "float", + "type": "number" + } + }, + "type": "object" + } + }, + "description": "Response for the LookupWeather RPC - represents the weather conditions at the requested location. This response represent both Hourly and Daily information, therefore the response is split in three sections Hourly, Daily and Shared. Only-Hourly, Only-Daily fields are marked as optional. For fields that are shared between Hourly and Daily information, some are always present so they are not marked as optional while the rest are marked as optional because they are not always available. ", + "properties": { + "airPressure": { + "$ref": "#/$defs/AirPressure", + "description": "The hourly air pressure conditions." + }, + "attribution": { + "$ref": "#/$defs/Attribution", + "description": "Required attribution to show with the weather." + }, + "cloudCover": { + "description": "The percentage of the sky covered by clouds (values from 0 to 100). define optional because it is not always available", + "format": "int32", + "type": "integer" + }, + "feelsLikeMaxTemperature": { + "$ref": "#/$defs/Temperature", + "description": "The maximum (high) feels-like temperature throughout the day." + }, + "feelsLikeMinTemperature": { + "$ref": "#/$defs/Temperature", + "description": "The minimum (low) feels-like temperature throughout the day." + }, + "feelsLikeTemperature": { + "$ref": "#/$defs/Temperature", + "description": "The hourly measure of how the temperature feels like." + }, + "heatIndex": { + "$ref": "#/$defs/Temperature", + "description": "The hourly heat index temperature." + }, + "maxHeatIndex": { + "$ref": "#/$defs/Temperature", + "description": "The maximum heat index temperature throughout the day." + }, + "maxTemperature": { + "$ref": "#/$defs/Temperature", + "description": "The maximum (high) temperature throughout the day." + }, + "minTemperature": { + "$ref": "#/$defs/Temperature", + "description": "The minimum (low) temperature throughout the day." + }, + "moonEvents": { + "$ref": "#/$defs/MoonEvents", + "description": "The events related to the moon (e.g. moonrise, moonset)." + }, + "precipitation": { + "$ref": "#/$defs/Precipitation", + "description": "The precipitation probability and amount of precipitation accumulated" + }, + "relativeHumidity": { + "description": "The percent of relative humidity (values from 0 to 100). define optional because it is not always available", + "format": "int32", + "type": "integer" + }, + "returnedLocation": { + "$ref": "#/$defs/Location", + "description": "Required. The location where the weather information is returned. This location is identical to the location in the request, but can be different from it if the requested location is a free text address that looks up to a coarse location (e.g. \"Mountain View, CA\")." + }, + "sunEvents": { + "$ref": "#/$defs/SunEvents", + "description": "The events related to the sun (e.g. sunrise, sunset)." + }, + "temperature": { + "$ref": "#/$defs/Temperature", + "description": "The hourly temperature" + }, + "thunderstormProbability": { + "description": "The thunderstorm probability (values from 0 to 100). define optional because it is not always available", + "format": "int32", + "type": "integer" + }, + "uvIndex": { + "description": "The maximum ultraviolet (UV) index. define optional because it is not always available", + "format": "int32", + "type": "integer" + }, + "weatherCondition": { + "$ref": "#/$defs/WeatherCondition", + "description": "The weather condition" + }, + "wind": { + "$ref": "#/$defs/Wind", + "description": "The wind conditions" + } + }, + "type": "object" + } + }, + { + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Computes a travel route between a specified origin and destination. **Supported Travel Modes:** DRIVE (default), WALK.\n\n**Input Requirements (CRITICAL):**\nRequires both **origin** and **destination**. Each must be provided using one of the following methods, nested within its respective field:\n\n* **address:** (string, e.g., 'Eiffel Tower, Paris'). Note: The more granular or specific the input address is, the better the results will be.\n\n* **lat_lng:** (object, {\"latitude\": number, \"longitude\": number})\n\n* **place_id:** (string, e.g., 'ChIJOwE_Id1w5EAR4Q27FkL6T_0') Note: This id can be obtained from the search_places tool.\nAny combination of input types is allowed (e.g., origin by address, destination by lat_lng). If either the origin or destination is missing, **you MUST ask the user for clarification** before attempting to call the tool.\n\n**Example Tool Call:**\n{\"origin\":{\"address\":\"Eiffel Tower\"},\"destination\":{\"place_id\":\"ChIJt_5xIthw5EARoJ71mGq7t74\"},\"travel_mode\":\"DRIVE\"}\n\n* The grounded output must be attributed to the source using the information from the `attribution` field when available.\n", + "inputSchema": { + "$defs": { + "LatLng": { + "description": "An object that represents a latitude/longitude pair. This is expressed as a pair of doubles to represent degrees latitude and degrees longitude. Unless specified otherwise, this object must conform to the WGS84 standard . Values must be within normalized ranges.", + "properties": { + "latitude": { + "description": "The latitude in degrees. It must be in the range [-90.0, +90.0].", + "format": "double", + "type": "number" + }, + "longitude": { + "description": "The longitude in degrees. It must be in the range [-180.0, +180.0].", + "format": "double", + "type": "number" + } + }, + "type": "object" + }, + "Waypoint": { + "description": "Encapsulates a waypoint. Waypoints mark both the beginning and end of a route.", + "properties": { + "address": { + "description": "Human readable address or a plus code. See https://plus.codes for details.", + "type": "string" + }, + "latLng": { + "$ref": "#/$defs/LatLng", + "description": "A point specified using geographic coordinates." + }, + "placeId": { + "description": "The Place ID associated with the waypoint.", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "ComputeRoutesRequest.", + "properties": { + "destination": { + "$ref": "#/$defs/Waypoint", + "description": "Required. Destination waypoint." + }, + "origin": { + "$ref": "#/$defs/Waypoint", + "description": "Required. Origin waypoint." + }, + "travelMode": { + "description": "Optional. Specifies the mode of transportation.", + "enum": [ + "ROUTE_TRAVEL_MODE_UNSPECIFIED", + "DRIVE", + "WALK" + ], + "type": "string", + "x-google-enum-descriptions": [ + "No travel mode specified. Defaults to `DRIVE`.", + "Travel by passenger car.", + "Travel by walking. NOTE: `WALK` routes are in beta and might sometimes be missing clear sidewalks or pedestrian paths. You must display this warning to the user for all walking that you display in your app." + ] + } + }, + "required": [ + "origin", + "destination" + ], + "type": "object" + }, + "name": "compute_routes", + "outputSchema": { + "$defs": { + "Attribution": { + "description": "Required attribution to show with maps content.", + "properties": { + "title": { + "description": "The title to display for the attribution.", + "type": "string" + }, + "url": { + "description": "The URL to link to for the attribution.", + "type": "string" + } + }, + "type": "object" + }, + "Route": { + "description": "Details about a route between two locations.", + "properties": { + "attribution": { + "$ref": "#/$defs/Attribution", + "description": "Required attribution to show with the route." + }, + "distanceMeters": { + "description": "The travel distance of the route, in meters.", + "format": "int32", + "type": "integer" + }, + "duration": { + "description": "The length of time needed to navigate the route.", + "format": "google-duration", + "type": "string" + } + }, + "type": "object" + } + }, + "description": "ComputeRoutesResponse.", + "properties": { + "routes": { + "description": "Contains routes between the requested origin and destination. Currently only one route is returned.", + "items": { + "$ref": "#/$defs/Route" + }, + "type": "array" + } + }, + "type": "object" + } + } + ] +} diff --git a/google-maps-official/server/tools/index.ts b/google-maps-official/server/tools/index.ts new file mode 100644 index 00000000..bb02c05e --- /dev/null +++ b/google-maps-official/server/tools/index.ts @@ -0,0 +1,9 @@ +import { wrapBackendSnapshot } from "@decocms/mcps-shared/google-mcp"; +import { BACKEND_URL, TOOL_SNAPSHOT } from "../constants.ts"; +import { getGoogleAccessToken } from "../lib/env.ts"; +import type { Env } from "../../shared/deco.gen.ts"; + +export const tools = wrapBackendSnapshot(TOOL_SNAPSHOT.tools, { + backendUrl: BACKEND_URL, + getAccessToken: (env) => getGoogleAccessToken(env as Env), +}); diff --git a/google-maps-official/shared/deco.gen.ts b/google-maps-official/shared/deco.gen.ts new file mode 100644 index 00000000..c535e482 --- /dev/null +++ b/google-maps-official/shared/deco.gen.ts @@ -0,0 +1,7 @@ +import { type DefaultEnv } from "@decocms/runtime"; +import type { Registry } from "@decocms/mcps-shared/registry"; +import { z } from "zod"; + +export const StateSchema = z.object({}); + +export type Env = DefaultEnv; diff --git a/google-maps-official/tsconfig.json b/google-maps-official/tsconfig.json new file mode 100644 index 00000000..f715197c --- /dev/null +++ b/google-maps-official/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "ES2024", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowJs": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["server", "shared"] +} diff --git a/google-workspace/TOOLS.md b/google-workspace/TOOLS.md index 62e6d246..af963a9c 100644 --- a/google-workspace/TOOLS.md +++ b/google-workspace/TOOLS.md @@ -2,9 +2,9 @@ _Auto-generated by `bun run generate-tools`. Do not edit by hand._ -33 tools across 5 services, gated behind 26 OAuth scopes. +33 tools across 5 backends, gated behind 26 OAuth scopes. -Tool names below are listed without the service prefix. The MCP exposes them as `_` (e.g. `calendar_list_events`). +Tool names below are listed without the service prefix. The MCP exposes them as `_`. ## calendar diff --git a/google-workspace/server/constants.ts b/google-workspace/server/constants.ts index 3dcf9586..c61e2da8 100644 --- a/google-workspace/server/constants.ts +++ b/google-workspace/server/constants.ts @@ -32,13 +32,7 @@ export const TOOL_SNAPSHOTS: Record = { people: peopleSnap as BackendSnapshot, }; -export interface BackendToolDefinition { - name: string; - description?: string; - inputSchema?: Record; - outputSchema?: Record; - annotations?: Record; -} +import type { BackendToolDefinition } from "@decocms/mcps-shared/google-mcp"; export interface BackendSnapshot { service: GoogleService; diff --git a/google-workspace/server/lib/wrap-tool.ts b/google-workspace/server/lib/wrap-tool.ts deleted file mode 100644 index 9d6a84e0..00000000 --- a/google-workspace/server/lib/wrap-tool.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { createPrivateTool } from "@decocms/runtime/tools"; -import { z } from "zod"; -import type { Env } from "../../shared/deco.gen.ts"; -import { - BACKEND_MCPS, - type BackendToolDefinition, - type GoogleService, -} from "../constants.ts"; -import { jsonSchemaToZod } from "./json-schema-to-zod.ts"; -import { getGoogleAccessToken } from "./env.ts"; -import { proxyMcpCall } from "./mcp-proxy.ts"; - -/** - * Build a deco tool factory from a Google MCP backend tool definition. - * The returned tool, when executed, proxies tools/call to the upstream - * backend with the user's Bearer token. - */ -export function wrapBackendTool( - service: GoogleService, - def: BackendToolDefinition, -) { - const id = `${service}_${def.name}`; - const inputSchema = jsonSchemaToZod(def.inputSchema ?? {}); - // The upstream content can be anything — we forward whatever the backend - // returns under the `content` field per the MCP spec. - const outputSchema = z.unknown(); - - return (env: Env) => - createPrivateTool({ - id, - description: def.description ?? `${service} ${def.name}`, - inputSchema, - outputSchema, - execute: async ({ context }) => { - const accessToken = getGoogleAccessToken(env); - const result = await proxyMcpCall( - BACKEND_MCPS[service], - def.name, - context, - accessToken, - ); - return result; - }, - }); -} diff --git a/google-workspace/server/scripts/generate-tools.ts b/google-workspace/server/scripts/generate-tools.ts index c17f98f9..2429eb6d 100644 --- a/google-workspace/server/scripts/generate-tools.ts +++ b/google-workspace/server/scripts/generate-tools.ts @@ -2,176 +2,26 @@ * Refresh per-service snapshots in server/tools/generated/ and the * human-readable TOOLS.md preview. * - * Fetches `tools/list` from each Google MCP backend and the PRM scopes_supported - * from each backend's RFC 9728 metadata. Both endpoints are reachable without - * authentication, so this script needs no Google credentials. - * * Run: `bun run generate-tools` */ -import { writeFile, mkdir } from "node:fs/promises"; -import { join, dirname } from "node:path"; +import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { BACKEND_MCPS, type GoogleService } from "../constants.ts"; +import { generateSnapshots } from "@decocms/mcps-shared/google-mcp/generate-snapshot"; +import { BACKEND_MCPS } from "../constants.ts"; const HERE = dirname(fileURLToPath(import.meta.url)); const OUT_DIR = join(HERE, "..", "tools", "generated"); const PACKAGE_ROOT = join(HERE, "..", ".."); const TOOLS_MD_PATH = join(PACKAGE_ROOT, "TOOLS.md"); -interface JsonRpcResponse { - jsonrpc: "2.0"; - id: number | string; - result?: { tools: unknown[] }; - error?: { code: number; message: string }; -} - -interface ProtectedResourceMetadata { - resource?: string; - authorization_servers?: string[]; - scopes_supported?: string[]; - bearer_methods_supported?: string[]; -} - -async function fetchToolsList(backendUrl: string): Promise { - const res = await fetch(backendUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json, text/event-stream", - }, - body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }), - }); - if (!res.ok) { - throw new Error( - `${backendUrl} tools/list HTTP ${res.status}: ${await res.text()}`, - ); - } - const json = (await res.json()) as JsonRpcResponse; - if (json.error) { - throw new Error(`${backendUrl} tools/list error: ${json.error.message}`); - } - return json.result?.tools ?? []; -} - -async function fetchScopes(backendUrl: string): Promise { - const u = new URL(backendUrl); - const prmUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`; - const res = await fetch(prmUrl, { headers: { Accept: "application/json" } }); - if (!res.ok) { - throw new Error(`${prmUrl} HTTP ${res.status}: ${await res.text()}`); - } - const prm = (await res.json()) as ProtectedResourceMetadata; - return prm.scopes_supported ?? []; -} - -interface BackendTool { - name: string; - description?: string; -} - -function renderToolsMarkdown( - snapshots: Record, -): string { - const services = Object.keys(snapshots) as GoogleService[]; - const total = services.reduce( - (acc, svc) => acc + new Set(snapshots[svc].tools.map((t) => t.name)).size, - 0, - ); - const allScopes = Array.from( - new Set(services.flatMap((svc) => snapshots[svc].scopes)), - ).sort(); - - const lines: string[] = []; - lines.push("# Google Workspace — Tool Catalog"); - lines.push(""); - lines.push( - "_Auto-generated by `bun run generate-tools`. Do not edit by hand._", - ); - lines.push(""); - lines.push( - `${total} tools across ${services.length} services, gated behind ${allScopes.length} OAuth scopes.`, - ); - lines.push(""); - lines.push( - "Tool names below are listed without the service prefix. The MCP exposes them as `_` (e.g. `calendar_list_events`).", - ); - lines.push(""); - - for (const service of services) { - const snap = snapshots[service]; - const seen = new Set(); - const unique = snap.tools.filter((t) => { - if (seen.has(t.name)) return false; - seen.add(t.name); - return true; - }); - - lines.push(`## ${service}`); - lines.push(""); - lines.push(`**Endpoint:** \`${BACKEND_MCPS[service]}\``); - lines.push(""); - lines.push("**Scopes:**"); - for (const scope of snap.scopes) lines.push(`- \`${scope}\``); - lines.push(""); - lines.push(`**Tools (${unique.length}):**`); - for (const tool of unique) { - const firstLine = (tool.description ?? "").split("\n")[0].trim(); - lines.push(`- \`${tool.name}\`${firstLine ? ` — ${firstLine}` : ""}`); - } - lines.push(""); - } - - return lines.join("\n"); -} - -async function main() { - await mkdir(OUT_DIR, { recursive: true }); - - const services = Object.keys(BACKEND_MCPS) as GoogleService[]; - const indexEntries: Record< - string, - { scopes: string[]; toolNames: string[] } - > = {}; - const snapshotsForMarkdown: Record< - GoogleService, - { scopes: string[]; tools: BackendTool[] } - > = {} as never; - - for (const service of services) { - const url = BACKEND_MCPS[service]; - process.stdout.write(`fetching ${service} (${url})... `); - const [tools, scopes] = await Promise.all([ - fetchToolsList(url), - fetchScopes(url), - ]); - const snap = { service, scopes, tools }; - await writeFile( - join(OUT_DIR, `${service}.json`), - JSON.stringify(snap, null, 2) + "\n", - ); - indexEntries[service] = { - scopes, - toolNames: tools.map((t) => (t as { name: string }).name), - }; - snapshotsForMarkdown[service] = { - scopes, - tools: tools as BackendTool[], - }; - console.log(`${tools.length} tools, ${scopes.length} scopes`); - } - - await writeFile( - join(OUT_DIR, "_index.json"), - JSON.stringify(indexEntries, null, 2) + "\n", - ); - - await writeFile(TOOLS_MD_PATH, renderToolsMarkdown(snapshotsForMarkdown)); - console.log(`\nWrote ${services.length} snapshots to ${OUT_DIR}`); - console.log(`Wrote tool catalog to ${TOOLS_MD_PATH}`); -} - -main().catch((err) => { - console.error(err); - process.exit(1); +await generateSnapshots({ + title: "Google Workspace — Tool Catalog", + outDir: OUT_DIR, + toolsMarkdownPath: TOOLS_MD_PATH, + prefixed: true, + backends: Object.entries(BACKEND_MCPS).map(([service, url]) => ({ + service, + url, + })), }); diff --git a/google-workspace/server/tools/generated/calendar.json b/google-workspace/server/tools/generated/calendar.json index f0e1b4c6..c04470ba 100644 --- a/google-workspace/server/tools/generated/calendar.json +++ b/google-workspace/server/tools/generated/calendar.json @@ -1,5 +1,6 @@ { "service": "calendar", + "url": "https://calendarmcp.googleapis.com/mcp/v1", "scopes": [ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.app.created", diff --git a/google-workspace/server/tools/generated/chat.json b/google-workspace/server/tools/generated/chat.json index 64510ae6..ffdc962e 100644 --- a/google-workspace/server/tools/generated/chat.json +++ b/google-workspace/server/tools/generated/chat.json @@ -1,5 +1,6 @@ { "service": "chat", + "url": "https://chatmcp.googleapis.com/mcp/v1", "scopes": [ "https://www.googleapis.com/auth/chat.spaces", "https://www.googleapis.com/auth/chat.spaces.readonly", diff --git a/google-workspace/server/tools/generated/drive.json b/google-workspace/server/tools/generated/drive.json index 88edc817..c1e22c2f 100644 --- a/google-workspace/server/tools/generated/drive.json +++ b/google-workspace/server/tools/generated/drive.json @@ -1,5 +1,6 @@ { "service": "drive", + "url": "https://drivemcp.googleapis.com/mcp/v1", "scopes": [ "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive.readonly", diff --git a/google-workspace/server/tools/generated/gmail.json b/google-workspace/server/tools/generated/gmail.json index 99d00eea..37264e12 100644 --- a/google-workspace/server/tools/generated/gmail.json +++ b/google-workspace/server/tools/generated/gmail.json @@ -1,5 +1,6 @@ { "service": "gmail", + "url": "https://gmailmcp.googleapis.com/mcp/v1", "scopes": [ "https://mail.google.com/", "https://www.googleapis.com/auth/gmail.modify", diff --git a/google-workspace/server/tools/generated/people.json b/google-workspace/server/tools/generated/people.json index be112190..5b481e56 100644 --- a/google-workspace/server/tools/generated/people.json +++ b/google-workspace/server/tools/generated/people.json @@ -1,5 +1,6 @@ { "service": "people", + "url": "https://people.googleapis.com/mcp/v1", "scopes": [ "https://www.googleapis.com/auth/directory.readonly", "https://www.googleapis.com/auth/userinfo.profile", diff --git a/google-workspace/server/tools/index.ts b/google-workspace/server/tools/index.ts index 082ac6a1..3532f7ae 100644 --- a/google-workspace/server/tools/index.ts +++ b/google-workspace/server/tools/index.ts @@ -2,22 +2,19 @@ * Aggregates tool factories for every Google service we proxy. Each backend * MCP's tools/list snapshot lives in ./generated/.json — re-run * `bun run generate-tools` to refresh. - * - * Some Google MCP backends (notably chat) return duplicate tool entries from - * tools/list. Dedupe by prefixed name to keep the runtime registration sane. */ -import { TOOL_SNAPSHOTS } from "../constants.ts"; -import { wrapBackendTool } from "../lib/wrap-tool.ts"; +import { wrapBackendSnapshot } from "@decocms/mcps-shared/google-mcp"; +import { BACKEND_MCPS, TOOL_SNAPSHOTS } from "../constants.ts"; +import { getGoogleAccessToken } from "../lib/env.ts"; +import type { Env } from "../../shared/deco.gen.ts"; -const seen = new Set(); -export const tools = Object.entries(TOOL_SNAPSHOTS).flatMap(([service, snap]) => - snap.tools - .filter((def) => { - const id = `${service}_${def.name}`; - if (seen.has(id)) return false; - seen.add(id); - return true; - }) - .map((def) => wrapBackendTool(service as keyof typeof TOOL_SNAPSHOTS, def)), +export const tools = ( + Object.keys(TOOL_SNAPSHOTS) as Array +).flatMap((service) => + wrapBackendSnapshot(TOOL_SNAPSHOTS[service].tools, { + backendUrl: BACKEND_MCPS[service], + prefix: service, + getAccessToken: (env) => getGoogleAccessToken(env as Env), + }), ); diff --git a/package.json b/package.json index cbf15617..83495cc6 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,16 @@ "github-repo-reports", "google-apps-script", "google-big-query", + "google-bigquery-official", "google-calendar", "google-calendar-sa", "google-docs", "google-drive", "google-forms", "google-gemini", + "google-gke-official", "google-gmail", + "google-maps-official", "google-meet", "google-search-console", "google-sheets", diff --git a/shared/google-mcp/generate-snapshot.ts b/shared/google-mcp/generate-snapshot.ts new file mode 100644 index 00000000..fa17fb50 --- /dev/null +++ b/shared/google-mcp/generate-snapshot.ts @@ -0,0 +1,202 @@ +/** + * Reusable codegen helper for "official Google MCP wrapper" packages. + * + * Each wrapper has a `server/scripts/generate-tools.ts` that imports this + * helper, passes its backend URL(s), and emits per-backend snapshot JSONs and + * a TOOLS.md preview alongside. + * + * The script needs no Google credentials — both `tools/list` and the RFC 9728 + * PRM endpoint are reachable without auth. + */ + +import { writeFile, mkdir } from "node:fs/promises"; +import { join } from "node:path"; + +interface JsonRpcResponse { + jsonrpc: "2.0"; + id: number | string; + result?: { tools: unknown[] }; + error?: { code: number; message: string }; +} + +interface ProtectedResourceMetadata { + resource?: string; + authorization_servers?: string[]; + scopes_supported?: string[]; +} + +interface BackendTool { + name: string; + description?: string; +} + +export interface BackendDescriptor { + /** Logical name. Used as the snapshot file name and as the tool prefix when multi-backend. */ + service: string; + /** Endpoint speaking MCP JSON-RPC. */ + url: string; +} + +export interface GenerateSnapshotsOptions { + /** One or more backends to fetch and snapshot. */ + backends: BackendDescriptor[]; + /** Directory where `.json` and `_index.json` are written. */ + outDir: string; + /** Path of the markdown file to render with the catalog. */ + toolsMarkdownPath: string; + /** Title used at the top of the generated markdown. */ + title: string; + /** + * Whether to emit prefixed names in TOOLS.md (`calendar_list_events`). + * Set true for multi-backend wrappers, false for single-backend. + */ + prefixed?: boolean; +} + +async function fetchToolsList(url: string): Promise { + const res = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json, text/event-stream", + }, + body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }), + }); + if (!res.ok) { + throw new Error( + `${url} tools/list HTTP ${res.status}: ${await res.text()}`, + ); + } + const json = (await res.json()) as JsonRpcResponse; + if (json.error) { + throw new Error(`${url} tools/list error: ${json.error.message}`); + } + return json.result?.tools ?? []; +} + +async function fetchScopes(url: string): Promise { + const u = new URL(url); + const prmUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`; + const res = await fetch(prmUrl, { headers: { Accept: "application/json" } }); + if (!res.ok) { + throw new Error(`${prmUrl} HTTP ${res.status}: ${await res.text()}`); + } + const prm = (await res.json()) as ProtectedResourceMetadata; + return prm.scopes_supported ?? []; +} + +function renderMarkdown( + title: string, + snapshots: Record< + string, + { url: string; scopes: string[]; tools: BackendTool[] } + >, + prefixed: boolean, +): string { + const services = Object.keys(snapshots); + const total = services.reduce((acc, svc) => { + const seen = new Set(snapshots[svc].tools.map((t) => t.name)); + return acc + seen.size; + }, 0); + const allScopes = Array.from( + new Set(services.flatMap((svc) => snapshots[svc].scopes)), + ).sort(); + + const lines: string[] = []; + lines.push(`# ${title}`); + lines.push(""); + lines.push( + "_Auto-generated by `bun run generate-tools`. Do not edit by hand._", + ); + lines.push(""); + lines.push( + `${total} tool${total === 1 ? "" : "s"} across ${services.length} backend${services.length === 1 ? "" : "s"}, gated behind ${allScopes.length} OAuth scope${allScopes.length === 1 ? "" : "s"}.`, + ); + lines.push(""); + + if (prefixed) { + lines.push( + "Tool names below are listed without the service prefix. The MCP exposes them as `_`.", + ); + lines.push(""); + } + + for (const service of services) { + const snap = snapshots[service]; + const seen = new Set(); + const unique = snap.tools.filter((t) => { + if (seen.has(t.name)) return false; + seen.add(t.name); + return true; + }); + + if (services.length > 1) { + lines.push(`## ${service}`); + lines.push(""); + } + lines.push(`**Endpoint:** \`${snap.url}\``); + lines.push(""); + lines.push("**Scopes:**"); + for (const scope of snap.scopes) lines.push(`- \`${scope}\``); + lines.push(""); + lines.push(`**Tools (${unique.length}):**`); + for (const tool of unique) { + const firstLine = (tool.description ?? "").split("\n")[0].trim(); + lines.push(`- \`${tool.name}\`${firstLine ? ` — ${firstLine}` : ""}`); + } + lines.push(""); + } + + return lines.join("\n"); +} + +export async function generateSnapshots( + opts: GenerateSnapshotsOptions, +): Promise { + await mkdir(opts.outDir, { recursive: true }); + + const indexEntries: Record< + string, + { scopes: string[]; toolNames: string[] } + > = {}; + const snapshotsForMarkdown: Record< + string, + { url: string; scopes: string[]; tools: BackendTool[] } + > = {}; + + for (const backend of opts.backends) { + process.stdout.write(`fetching ${backend.service} (${backend.url})... `); + const [tools, scopes] = await Promise.all([ + fetchToolsList(backend.url), + fetchScopes(backend.url), + ]); + const snap = { service: backend.service, url: backend.url, scopes, tools }; + await writeFile( + join(opts.outDir, `${backend.service}.json`), + JSON.stringify(snap, null, 2) + "\n", + ); + indexEntries[backend.service] = { + scopes, + toolNames: tools.map((t) => (t as { name: string }).name), + }; + snapshotsForMarkdown[backend.service] = { + url: backend.url, + scopes, + tools: tools as BackendTool[], + }; + console.log(`${tools.length} tools, ${scopes.length} scopes`); + } + + await writeFile( + join(opts.outDir, "_index.json"), + JSON.stringify(indexEntries, null, 2) + "\n", + ); + + await writeFile( + opts.toolsMarkdownPath, + renderMarkdown(opts.title, snapshotsForMarkdown, opts.prefixed ?? false), + ); + + console.log(`\nWrote ${opts.backends.length} snapshot(s) to ${opts.outDir}`); + console.log(`Wrote tool catalog to ${opts.toolsMarkdownPath}`); +} diff --git a/shared/google-mcp/index.ts b/shared/google-mcp/index.ts new file mode 100644 index 00000000..5955c2ef --- /dev/null +++ b/shared/google-mcp/index.ts @@ -0,0 +1,23 @@ +/** + * Helpers for wrapping Google's official MCP servers (which speak JSON-RPC + * but don't support Dynamic Client Registration). A wrapper MCP holds the + * Google OAuth client ID/secret server-side, runs the standard PKCE flow via + * `createGoogleOAuth`, and proxies `tools/call` to the upstream endpoint. + * + * Used by `google-workspace` (Calendar/Chat/Drive/Gmail/People) and the + * `google-*-official` single-service wrappers. + */ + +export { proxyMcpCall } from "./proxy.ts"; +export { jsonSchemaToZod } from "./json-schema-to-zod.ts"; +export { + wrapBackendTool, + wrapBackendSnapshot, + type BackendToolDefinition, + type WrapToolOptions, +} from "./wrap-tool.ts"; +export { + generateSnapshots, + type BackendDescriptor, + type GenerateSnapshotsOptions, +} from "./generate-snapshot.ts"; diff --git a/google-workspace/server/lib/json-schema-to-zod.ts b/shared/google-mcp/json-schema-to-zod.ts similarity index 89% rename from google-workspace/server/lib/json-schema-to-zod.ts rename to shared/google-mcp/json-schema-to-zod.ts index 4c3f5f0f..7f9b0465 100644 --- a/google-workspace/server/lib/json-schema-to-zod.ts +++ b/shared/google-mcp/json-schema-to-zod.ts @@ -22,13 +22,10 @@ export function jsonSchemaToZod(schema: unknown): z.ZodTypeAny { if (!schema || typeof schema !== "object") return z.unknown(); const s = schema as JsonSchema; - // Tagged unions / polymorphism — pass through; backend validates. if (Array.isArray(s.oneOf) || Array.isArray(s.anyOf)) { return z.unknown().describe(s.description ?? ""); } - // Some Google schemas use type as an array, e.g. ["string", "null"]. Pick the - // first non-null type and rely on the backend for nullability. const type = Array.isArray(s.type) ? (s.type.find((t) => t !== "null") ?? "unknown") : s.type; @@ -61,7 +58,6 @@ function objectSchema(s: JsonSchema): z.ZodTypeAny { const child = jsonSchemaToZod(propSchema); shape[key] = required.has(key) ? child : child.optional(); } - // passthrough() lets the backend accept fields we may not know about. return withDescription(z.object(shape).passthrough(), s.description); } diff --git a/google-workspace/server/lib/mcp-proxy.ts b/shared/google-mcp/proxy.ts similarity index 82% rename from google-workspace/server/lib/mcp-proxy.ts rename to shared/google-mcp/proxy.ts index 36f843bf..da96e481 100644 --- a/google-workspace/server/lib/mcp-proxy.ts +++ b/shared/google-mcp/proxy.ts @@ -1,9 +1,10 @@ /** - * JSON-RPC proxy for Google's official MCP servers. + * Generic JSON-RPC proxy for Google's official MCP servers. * - * Each Google MCP endpoint (calendarmcp.googleapis.com, gmailmcp..., etc.) speaks - * JSON-RPC 2.0 over HTTP and authenticates via Bearer tokens. We forward the user's - * access token and pass through the result/error. + * Each Google MCP endpoint (calendarmcp.googleapis.com, bigquery.googleapis.com, + * container.googleapis.com, etc.) speaks JSON-RPC 2.0 over HTTP and authenticates + * via Bearer tokens. This helper forwards the user's access token and surfaces + * 401 / 403 / JSON-RPC errors with re-auth hints. */ interface JsonRpcSuccess { diff --git a/shared/google-mcp/wrap-tool.ts b/shared/google-mcp/wrap-tool.ts new file mode 100644 index 00000000..d186261d --- /dev/null +++ b/shared/google-mcp/wrap-tool.ts @@ -0,0 +1,78 @@ +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import { jsonSchemaToZod } from "./json-schema-to-zod.ts"; +import { proxyMcpCall } from "./proxy.ts"; + +export interface BackendToolDefinition { + name: string; + description?: string; + inputSchema?: Record; + outputSchema?: Record; + annotations?: Record; +} + +export interface WrapToolOptions { + /** Backend MCP URL to proxy tools/call to (e.g. https://bigquery.googleapis.com/mcp). */ + backendUrl: string; + /** + * Optional prefix prepended to every tool name. Useful when one MCP wraps + * multiple backends (`calendar_list_events`, `gmail_search_threads`, ...). + * For single-backend wrappers, leave empty. + */ + prefix?: string; + /** + * Reads the user's Google access token out of the runtime env on each call. + * Typically `(env) => env.MESH_REQUEST_CONTEXT?.authorization` plus a guard. + */ + getAccessToken: (env: unknown) => string; +} + +/** + * Build a deco tool factory from a Google MCP backend tool definition. + * The returned factory takes `env` and produces a `createPrivateTool` whose + * execute proxies tools/call to `opts.backendUrl` with the user's Bearer token. + */ +export function wrapBackendTool( + def: BackendToolDefinition, + opts: WrapToolOptions, +) { + const id = opts.prefix ? `${opts.prefix}_${def.name}` : def.name; + const inputSchema = jsonSchemaToZod(def.inputSchema ?? {}); + const outputSchema = z.unknown(); + + return (env: unknown) => + createPrivateTool({ + id, + description: def.description ?? id, + inputSchema, + outputSchema, + execute: async ({ context }) => { + const accessToken = opts.getAccessToken(env); + return await proxyMcpCall( + opts.backendUrl, + def.name, + context, + accessToken, + ); + }, + }); +} + +/** + * Convenience helper: wrap an entire snapshot ({ tools: BackendToolDefinition[] }) + * deduping by tool id (some Google backends return duplicates in tools/list). + */ +export function wrapBackendSnapshot( + tools: BackendToolDefinition[], + opts: WrapToolOptions, +) { + const seen = new Set(); + return tools + .filter((def) => { + const id = opts.prefix ? `${opts.prefix}_${def.name}` : def.name; + if (seen.has(id)) return false; + seen.add(id); + return true; + }) + .map((def) => wrapBackendTool(def, opts)); +} diff --git a/shared/package.json b/shared/package.json index d8a02747..46d76651 100644 --- a/shared/package.json +++ b/shared/package.json @@ -20,6 +20,8 @@ "./serve": "./serve.ts", "./registry": "./registry.ts", "./google-oauth": "./google-oauth.ts", + "./google-mcp": "./google-mcp/index.ts", + "./google-mcp/generate-snapshot": "./google-mcp/generate-snapshot.ts", "./whatsapp": "./whatsapp/index.ts", "./api-key-manager": "./api-key-manager.ts", "./mesh-chat": "./mesh-chat/index.ts"