From 9afc0cacddf2523f3c2606b66a0f0b2a0ff16ee4 Mon Sep 17 00:00:00 2001 From: Twisha Bansal Date: Tue, 14 Apr 2026 13:09:29 +0530 Subject: [PATCH 1/5] feat: add support for skills --- .../workflows/skills-validate-fallback.yml | 37 ++++++ .github/workflows/skills-validate.yml | 56 +++++++++ gemini-extension.json | 10 -- generate-skills-repo.sh | 42 +++++++ skills/bigquery-analytics/SKILL.md | 101 ++++++++++++++++ .../scripts/analyze_contribution.js | 107 +++++++++++++++++ .../scripts/ask_data_insights.js | 107 +++++++++++++++++ skills/bigquery-analytics/scripts/forecast.js | 107 +++++++++++++++++ .../scripts/search_catalog.js | 107 +++++++++++++++++ skills/bigquery-data/SKILL.md | 108 ++++++++++++++++++ skills/bigquery-data/scripts/execute_sql.js | 107 +++++++++++++++++ .../bigquery-data/scripts/get_dataset_info.js | 107 +++++++++++++++++ .../bigquery-data/scripts/get_table_info.js | 107 +++++++++++++++++ .../bigquery-data/scripts/list_dataset_ids.js | 107 +++++++++++++++++ .../bigquery-data/scripts/list_table_ids.js | 107 +++++++++++++++++ .../bigquery-data/scripts/search_catalog.js | 107 +++++++++++++++++ toolbox_version.txt | 1 - tools.yaml | 61 ---------- 18 files changed, 1414 insertions(+), 72 deletions(-) create mode 100644 .github/workflows/skills-validate-fallback.yml create mode 100644 .github/workflows/skills-validate.yml create mode 100755 generate-skills-repo.sh create mode 100644 skills/bigquery-analytics/SKILL.md create mode 100755 skills/bigquery-analytics/scripts/analyze_contribution.js create mode 100755 skills/bigquery-analytics/scripts/ask_data_insights.js create mode 100755 skills/bigquery-analytics/scripts/forecast.js create mode 100755 skills/bigquery-analytics/scripts/search_catalog.js create mode 100644 skills/bigquery-data/SKILL.md create mode 100755 skills/bigquery-data/scripts/execute_sql.js create mode 100755 skills/bigquery-data/scripts/get_dataset_info.js create mode 100755 skills/bigquery-data/scripts/get_table_info.js create mode 100755 skills/bigquery-data/scripts/list_dataset_ids.js create mode 100755 skills/bigquery-data/scripts/list_table_ids.js create mode 100755 skills/bigquery-data/scripts/search_catalog.js delete mode 100644 toolbox_version.txt delete mode 100644 tools.yaml diff --git a/.github/workflows/skills-validate-fallback.yml b/.github/workflows/skills-validate-fallback.yml new file mode 100644 index 0000000..b49eb62 --- /dev/null +++ b/.github/workflows/skills-validate-fallback.yml @@ -0,0 +1,37 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: lint + +on: + push: + paths-ignore: + - "skills/**" + pull_request: + paths-ignore: + - "skills/**" + pull_request_target: + types: [labeled] + paths-ignore: + - "skills/**" + workflow_dispatch: + +jobs: + skills-validate: + runs-on: ubuntu-latest + steps: + - name: Skip Skill Validation + run: | + echo "No changes detected in 'skills/' directory. Skipping validation." + echo "This job ensures the required 'skills-validate' status check passes." diff --git a/.github/workflows/skills-validate.yml b/.github/workflows/skills-validate.yml new file mode 100644 index 0000000..ebcae91 --- /dev/null +++ b/.github/workflows/skills-validate.yml @@ -0,0 +1,56 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Validate Skills + +on: + push: + paths: + - "skills/**" + pull_request: + paths: + - "skills/**" + pull_request_target: + types: [labeled] + paths: + - "skills/**" + +jobs: + skills-validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: "3.11" + + - name: Install skills-ref + run: | + pip install "git+https://github.com/agentskills/agentskills.git#subdirectory=skills-ref" + + - name: Validate Skills + run: | + failed=0 + for skill_dir in skills/*/; do + if [ -d "$skill_dir" ]; then + echo "Validating $skill_dir..." + if ! skills-ref validate "$skill_dir"; then + echo "Validation failed for $skill_dir" + failed=1 + fi + fi + done + exit $failed diff --git a/gemini-extension.json b/gemini-extension.json index baef545..3b9f492 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -2,16 +2,6 @@ "name": "bigquery-data-analytics", "version": "0.1.7", "description": "Connect, query, and generate data insights for BigQuery datasets and data.", - "mcpServers": { - "bigquery_data_analytics": { - "command": "${extensionPath}${/}toolbox", - "args": [ - "--tools-file", - "${extensionPath}${/}tools.yaml", - "--stdio" - ] - } - }, "contextFileName": "BIGQUERY.md", "settings": [ { diff --git a/generate-skills-repo.sh b/generate-skills-repo.sh new file mode 100755 index 0000000..1513e4f --- /dev/null +++ b/generate-skills-repo.sh @@ -0,0 +1,42 @@ +export BIGQUERY_PROJECT="toolbox-testing-438616" + +export VERSION=1.1.0 +# toolbox binary is already downloaded as toolbox2 + +./toolbox2 --prebuilt bigquery skills-generate \ + --name "bigquery-data" \ + --description "Use these skills when you need to handle large-scale data exploration and dataset management. Use when users need to find data assets or run SQL at scale. Provides metadata discovery and query execution across the data warehouse." \ + --toolset=data \ + --license-header "// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the \"License\"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an \"AS IS\" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License." \ + --additional-notes="Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence." + +./toolbox2 --prebuilt bigquery skills-generate \ + --name "bigquery-analytics" \ + --description "Use these skills when you need to handle advanced data intelligence and predictive tasks. Use when a user asks \"why\" data changed or needs future projections. Provides automated insight generation and time-series forecasting." \ + --toolset=analytics \ + --license-header "// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the \"License\"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an \"AS IS\" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License." \ + --additional-notes="Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence." diff --git a/skills/bigquery-analytics/SKILL.md b/skills/bigquery-analytics/SKILL.md new file mode 100644 index 0000000..07c4012 --- /dev/null +++ b/skills/bigquery-analytics/SKILL.md @@ -0,0 +1,101 @@ +--- +name: bigquery-analytics +description: Use these skills when you need to handle advanced data intelligence and predictive tasks. Use when a user asks "why" data changed or needs future projections. Provides automated insight generation and time-series forecasting. +--- + +## Usage + +All scripts can be executed using Node.js. Replace `` and `` with actual values. + +**Bash:** +`node /scripts/.js '{"": ""}'` + +**PowerShell:** +`node /scripts/.js '{\"\": \"\"}'` + +Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence. + + +## Scripts + + +### analyze_contribution + +Use this skill to analyze the contribution about changes to key metrics in multi-dimensional data. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| input_data | string | The data that contain the test and control data to analyze. Can be a fully qualified BigQuery table ID or a SQL query. | Yes | | +| contribution_metric | string | The name of the column that contains the metric to analyze. + Provides the expression to use to calculate the metric you are analyzing. + To calculate a summable metric, the expression must be in the form SUM(metric_column_name), + where metric_column_name is a numeric data type. + + To calculate a summable ratio metric, the expression must be in the form + SUM(numerator_metric_column_name)/SUM(denominator_metric_column_name), + where numerator_metric_column_name and denominator_metric_column_name are numeric data types. + + To calculate a summable by category metric, the expression must be in the form + SUM(metric_sum_column_name)/COUNT(DISTINCT categorical_column_name). The summed column must be a numeric data type. + The categorical column must have type BOOL, DATE, DATETIME, TIME, TIMESTAMP, STRING, or INT64. | Yes | | +| is_test_col | string | The name of the column that identifies whether a row is in the test or control group. | Yes | | +| dimension_id_cols | array | An array of column names that uniquely identify each dimension. | No | | +| top_k_insights_by_apriori_support | integer | The number of top insights to return, ranked by apriori support. | No | `30` | +| pruning_method | string | The method to use for pruning redundant insights. Can be 'NO_PRUNING' or 'PRUNE_REDUNDANT_INSIGHTS'. | No | `PRUNE_REDUNDANT_INSIGHTS` | + + +--- + +### ask_data_insights + +Use this skill to perform data analysis, get insights, +or answer complex questions about the contents of specific +BigQuery tables. + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| user_query_with_context | string | The user's question, potentially including conversation history and system instructions for context. | Yes | | +| table_references | string | A JSON string of a list of BigQuery tables to use as context. Each object in the list must contain 'projectId', 'datasetId', and 'tableId'. Example: '[{"projectId": "my-gcp-project", "datasetId": "my_dataset", "tableId": "my_table"}]'. | Yes | | + + +--- + +### forecast + +Use this skill to forecast time series data. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| history_data | string | The table id or the query of the history time series data. | Yes | | +| timestamp_col | string | The name of the time series timestamp column. | Yes | | +| data_col | string | The name of the time series data column. | Yes | | +| id_cols | array | An array of the time series id column names. | No | `[]` | +| horizon | integer | The number of forecasting steps. | No | `10` | + + +--- + +### search_catalog + +Use this skill to find tables, views, models, routines or connections. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| prompt | string | Prompt representing search intention. Do not rewrite the prompt. | Yes | | +| datasetIds | array | Array of dataset IDs. | No | `[]` | +| projectIds | array | Array of project IDs. | No | `[]` | +| types | array | Array of data types to filter by. | No | `[]` | +| pageSize | integer | Number of results in the search page. | No | `5` | + + +--- + diff --git a/skills/bigquery-analytics/scripts/analyze_contribution.js b/skills/bigquery-analytics/scripts/analyze_contribution.js new file mode 100755 index 0000000..ed303ad --- /dev/null +++ b/skills/bigquery-analytics/scripts/analyze_contribution.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "analyze_contribution"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-analytics/scripts/ask_data_insights.js b/skills/bigquery-analytics/scripts/ask_data_insights.js new file mode 100755 index 0000000..d083ae7 --- /dev/null +++ b/skills/bigquery-analytics/scripts/ask_data_insights.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "ask_data_insights"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-analytics/scripts/forecast.js b/skills/bigquery-analytics/scripts/forecast.js new file mode 100755 index 0000000..e2c19e2 --- /dev/null +++ b/skills/bigquery-analytics/scripts/forecast.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "forecast"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-analytics/scripts/search_catalog.js b/skills/bigquery-analytics/scripts/search_catalog.js new file mode 100755 index 0000000..4e50ff9 --- /dev/null +++ b/skills/bigquery-analytics/scripts/search_catalog.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "search_catalog"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/SKILL.md b/skills/bigquery-data/SKILL.md new file mode 100644 index 0000000..2884802 --- /dev/null +++ b/skills/bigquery-data/SKILL.md @@ -0,0 +1,108 @@ +--- +name: bigquery-data +description: Use these skills when you need to handle large-scale data exploration and dataset management. Use when users need to find data assets or run SQL at scale. Provides metadata discovery and query execution across the data warehouse. +--- + +## Usage + +All scripts can be executed using Node.js. Replace `` and `` with actual values. + +**Bash:** +`node /scripts/.js '{"": ""}'` + +**PowerShell:** +`node /scripts/.js '{\"\": \"\"}'` + +Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence. + + +## Scripts + + +### execute_sql + +Use this skill to execute sql statement. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| sql | string | The SQL to execute. | Yes | | +| dry_run | boolean | If set to true, the query will be validated and information about the execution will be returned without running the query. Defaults to false. | No | `false` | + + +--- + +### get_dataset_info + +Use this skill to get dataset metadata. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The Google Cloud project ID containing the dataset. | No | | +| dataset | string | The dataset to get metadata information. Can be in `project.dataset` format. | Yes | | + + +--- + +### get_table_info + +Use this skill to get table metadata. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The Google Cloud project ID containing the dataset and table. | No | | +| dataset | string | The table's parent dataset. | Yes | | +| table | string | The table to get metadata information. | Yes | | + + +--- + +### list_dataset_ids + +Use this skill to list datasets. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The Google Cloud project to list dataset ids. | No | | + + +--- + +### list_table_ids + +Use this skill to list tables. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The Google Cloud project ID containing the dataset. | No | | +| dataset | string | The dataset to list table ids. | Yes | | + + +--- + +### search_catalog + +Use this skill to find tables, views, models, routines or connections. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| prompt | string | Prompt representing search intention. Do not rewrite the prompt. | Yes | | +| datasetIds | array | Array of dataset IDs. | No | `[]` | +| projectIds | array | Array of project IDs. | No | `[]` | +| types | array | Array of data types to filter by. | No | `[]` | +| pageSize | integer | Number of results in the search page. | No | `5` | + + +--- + diff --git a/skills/bigquery-data/scripts/execute_sql.js b/skills/bigquery-data/scripts/execute_sql.js new file mode 100755 index 0000000..f7e1695 --- /dev/null +++ b/skills/bigquery-data/scripts/execute_sql.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "execute_sql"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/scripts/get_dataset_info.js b/skills/bigquery-data/scripts/get_dataset_info.js new file mode 100755 index 0000000..208567c --- /dev/null +++ b/skills/bigquery-data/scripts/get_dataset_info.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "get_dataset_info"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/scripts/get_table_info.js b/skills/bigquery-data/scripts/get_table_info.js new file mode 100755 index 0000000..5ec12a2 --- /dev/null +++ b/skills/bigquery-data/scripts/get_table_info.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "get_table_info"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/scripts/list_dataset_ids.js b/skills/bigquery-data/scripts/list_dataset_ids.js new file mode 100755 index 0000000..af381e5 --- /dev/null +++ b/skills/bigquery-data/scripts/list_dataset_ids.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "list_dataset_ids"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/scripts/list_table_ids.js b/skills/bigquery-data/scripts/list_table_ids.js new file mode 100755 index 0000000..c8036e5 --- /dev/null +++ b/skills/bigquery-data/scripts/list_table_ids.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "list_table_ids"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/skills/bigquery-data/scripts/search_catalog.js b/skills/bigquery-data/scripts/search_catalog.js new file mode 100755 index 0000000..4e50ff9 --- /dev/null +++ b/skills/bigquery-data/scripts/search_catalog.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const { spawn, execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const toolName = "search_catalog"; +const configArgs = ["--prebuilt", "bigquery"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'BIGQUERY_LOCATION', + 'BIGQUERY_USE_CLIENT_OAUTH', + 'BIGQUERY_SCOPES', + 'BIGQUERY_MAX_QUERY_RESULT_ROWS', + 'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT', +]; + + +function mergeEnvVars(env) { + if (process.env.GEMINI_CLI === '1') { + const envPath = path.resolve(__dirname, '../../../.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf-8'); + envContent.split('\n').forEach(line => { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const splitIdx = trimmed.indexOf('='); + if (splitIdx !== -1) { + const key = trimmed.slice(0, splitIdx).trim(); + let value = trimmed.slice(splitIdx + 1).trim(); + value = value.replace(/(^['"]|['"]$)/g, ''); + if (env[key] === undefined) { + env[key] = value; + } + } + } + }); + } + } else if (process.env.CLAUDECODE === '1') { + const prefix = 'CLAUDE_PLUGIN_OPTION_'; + for (const key in process.env) { + if (key.startsWith(prefix)) { + env[key.substring(prefix.length)] = process.env[key]; + } + } + } +} + +function prepareEnvironment() { + let env = { ...process.env }; + let userAgent = "skills"; + if (process.env.GEMINI_CLI === '1') { + userAgent = "skills-geminicli"; + } else if (process.env.CLAUDECODE === '1') { + userAgent = "skills-claudecode"; + } else if (process.env.CODEX_CI === '1') { + userAgent = "skills-codex"; + } + mergeEnvVars(env); + + OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { + if (env[varName] === '') { + delete env[varName]; + } + }); + + + return { env, userAgent }; +} + +function main() { + const { env, userAgent } = prepareEnvironment(); + const args = process.argv.slice(2); + + const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; + const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; + const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; + + const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); + + + child.on('close', (code) => { + process.exit(code); + }); + + child.on('error', (err) => { + console.error("Error executing toolbox:", err); + process.exit(1); + }); +} + +main(); diff --git a/toolbox_version.txt b/toolbox_version.txt deleted file mode 100644 index 4e8f395..0000000 --- a/toolbox_version.txt +++ /dev/null @@ -1 +0,0 @@ -0.26.0 diff --git a/tools.yaml b/tools.yaml deleted file mode 100644 index 83b46d2..0000000 --- a/tools.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -sources: - bigquery-source: - kind: "bigquery" - project: ${BIGQUERY_PROJECT} - location: ${BIGQUERY_LOCATION:} - useClientOAuth: ${BIGQUERY_USE_CLIENT_OAUTH:false} - -tools: - analyze_contribution: - kind: bigquery-analyze-contribution - source: bigquery-source - description: Use this tool to analyze the contribution about changes to key metrics in multi-dimensional data. - - execute_sql: - kind: bigquery-execute-sql - source: bigquery-source - description: Use this tool to execute sql statement. - - forecast: - kind: bigquery-forecast - source: bigquery-source - description: Use this tool to forecast time series data. - - get_dataset_info: - kind: bigquery-get-dataset-info - source: bigquery-source - description: Use this tool to get dataset metadata. - - get_table_info: - kind: bigquery-get-table-info - source: bigquery-source - description: Use this tool to get table metadata. - - list_dataset_ids: - kind: bigquery-list-dataset-ids - source: bigquery-source - description: Use this tool to list datasets. - - list_table_ids: - kind: bigquery-list-table-ids - source: bigquery-source - description: Use this tool to list tables. - - search_catalog: - kind: bigquery-search-catalog - source: bigquery-source - description: Use this tool to find tables, views, models, routines or connections. From 44edc63662ef04d3fac50322581b64a43bb8c803 Mon Sep 17 00:00:00 2001 From: Twisha Bansal Date: Tue, 14 Apr 2026 13:46:19 +0530 Subject: [PATCH 2/5] feat: add new optional bigquery settings --- gemini-extension.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/gemini-extension.json b/gemini-extension.json index 3b9f492..90e6e31 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -13,6 +13,26 @@ "name": "Location", "description": "(Optional) Location of the BigQuery resources", "envVar": "BIGQUERY_LOCATION" + }, + { + "name": "Use Client OAuth", + "description": "(Optional) Whether to use client OAuth (default: false)", + "envVar": "BIGQUERY_USE_CLIENT_OAUTH" + }, + { + "name": "Scopes", + "description": "(Optional) Scopes for the BigQuery client", + "envVar": "BIGQUERY_SCOPES" + }, + { + "name": "Max Query Result Rows", + "description": "(Optional) Maximum number of query result rows (default: 50)", + "envVar": "BIGQUERY_MAX_QUERY_RESULT_ROWS" + }, + { + "name": "Impersonate Service Account", + "description": "(Optional) Service account to impersonate", + "envVar": "BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT" } ] } \ No newline at end of file From 91bee55212d31aec80d1da91ba895335b4f1e3d7 Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:48:04 +0530 Subject: [PATCH 3/5] Delete generate-skills-repo.sh --- generate-skills-repo.sh | 42 ----------------------------------------- 1 file changed, 42 deletions(-) delete mode 100755 generate-skills-repo.sh diff --git a/generate-skills-repo.sh b/generate-skills-repo.sh deleted file mode 100755 index 1513e4f..0000000 --- a/generate-skills-repo.sh +++ /dev/null @@ -1,42 +0,0 @@ -export BIGQUERY_PROJECT="toolbox-testing-438616" - -export VERSION=1.1.0 -# toolbox binary is already downloaded as toolbox2 - -./toolbox2 --prebuilt bigquery skills-generate \ - --name "bigquery-data" \ - --description "Use these skills when you need to handle large-scale data exploration and dataset management. Use when users need to find data assets or run SQL at scale. Provides metadata discovery and query execution across the data warehouse." \ - --toolset=data \ - --license-header "// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the \"License\"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an \"AS IS\" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License." \ - --additional-notes="Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence." - -./toolbox2 --prebuilt bigquery skills-generate \ - --name "bigquery-analytics" \ - --description "Use these skills when you need to handle advanced data intelligence and predictive tasks. Use when a user asks \"why\" data changed or needs future projections. Provides automated insight generation and time-series forecasting." \ - --toolset=analytics \ - --license-header "// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the \"License\"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an \"AS IS\" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License." \ - --additional-notes="Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence." From bf12b11ed793a7f86117f99d84a2b305c02da84e Mon Sep 17 00:00:00 2001 From: Twisha Bansal Date: Tue, 14 Apr 2026 13:50:33 +0530 Subject: [PATCH 4/5] revert delete --- toolbox_version.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 toolbox_version.txt diff --git a/toolbox_version.txt b/toolbox_version.txt new file mode 100644 index 0000000..9084fa2 --- /dev/null +++ b/toolbox_version.txt @@ -0,0 +1 @@ +1.1.0 From 04b845b600a5fd128e6cd149d3603d7a5ac9c8a9 Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Tue, 14 Apr 2026 22:50:07 +0530 Subject: [PATCH 5/5] Update gemini-extension.json --- gemini-extension.json | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/gemini-extension.json b/gemini-extension.json index 90e6e31..9b9dd8c 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -13,26 +13,6 @@ "name": "Location", "description": "(Optional) Location of the BigQuery resources", "envVar": "BIGQUERY_LOCATION" - }, - { - "name": "Use Client OAuth", - "description": "(Optional) Whether to use client OAuth (default: false)", - "envVar": "BIGQUERY_USE_CLIENT_OAUTH" - }, - { - "name": "Scopes", - "description": "(Optional) Scopes for the BigQuery client", - "envVar": "BIGQUERY_SCOPES" - }, - { - "name": "Max Query Result Rows", - "description": "(Optional) Maximum number of query result rows (default: 50)", - "envVar": "BIGQUERY_MAX_QUERY_RESULT_ROWS" - }, - { - "name": "Impersonate Service Account", - "description": "(Optional) Service account to impersonate", - "envVar": "BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT" } ] -} \ No newline at end of file +}