Skip to content

Commit b7d4b3e

Browse files
authored
Merge branch 'main' into docs/update-to-skills
2 parents c66a886 + ce52772 commit b7d4b3e

18 files changed

Lines changed: 1416 additions & 72 deletions

.claude-plugin/marketplace.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "bigquery-data-analytics-marketplace",
3+
"owner": {
4+
"name": "Google LLC",
5+
"email": "data-cloud-ai-integrations@google.com"
6+
},
7+
"metadata": {
8+
"description": "Agent skills for BigQuery to connect, query, and generate data insights."
9+
},
10+
"plugins": [
11+
{
12+
"name": "bigquery-data-analytics",
13+
"source": "./"
14+
}
15+
]
16+
}

.claude-plugin/plugin.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "bigquery-data-analytics",
3+
"version": "0.1.7",
4+
"description": "Connect, query, and generate data insights for BigQuery datasets and data.",
5+
"author": {
6+
"name": "Google LLC",
7+
"email": "data-cloud-ai-integrations@google.com"
8+
},
9+
"homepage": "https://cloud.google.com/bigquery",
10+
"license": "Apache-2.0",
11+
"repository": "https://github.com/gemini-cli-extensions/bigquery-data-analytics",
12+
"skills": "./skills/",
13+
"userConfig": {
14+
"bigquery_project": {
15+
"title": "Project ID",
16+
"description": "ID of the Google Cloud project",
17+
"type": "string",
18+
"sensitive": false
19+
},
20+
"bigquery_location": {
21+
"title": "Location",
22+
"description": "(Optional) Location of the BigQuery resources",
23+
"type": "string",
24+
"sensitive": false
25+
}
26+
}
27+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: lint
16+
17+
on:
18+
push:
19+
paths-ignore:
20+
- "skills/**"
21+
pull_request:
22+
paths-ignore:
23+
- "skills/**"
24+
pull_request_target:
25+
types: [labeled]
26+
paths-ignore:
27+
- "skills/**"
28+
workflow_dispatch:
29+
30+
jobs:
31+
skills-validate:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Skip Skill Validation
35+
run: |
36+
echo "No changes detected in 'skills/' directory. Skipping validation."
37+
echo "This job ensures the required 'skills-validate' status check passes."
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Validate Skills
16+
17+
on:
18+
push:
19+
paths:
20+
- "skills/**"
21+
pull_request:
22+
paths:
23+
- "skills/**"
24+
pull_request_target:
25+
types: [labeled]
26+
paths:
27+
- "skills/**"
28+
29+
jobs:
30+
skills-validate:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
34+
35+
- name: Set up Python
36+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
37+
with:
38+
python-version: "3.11"
39+
40+
- name: Install skills-ref
41+
run: |
42+
pip install "git+https://github.com/agentskills/agentskills.git#subdirectory=skills-ref"
43+
44+
- name: Validate Skills
45+
run: |
46+
failed=0
47+
for skill_dir in skills/*/; do
48+
if [ -d "$skill_dir" ]; then
49+
echo "Validating $skill_dir..."
50+
if ! skills-ref validate "$skill_dir"; then
51+
echo "Validation failed for $skill_dir"
52+
failed=1
53+
fi
54+
fi
55+
done
56+
exit $failed

gemini-extension.json

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@
22
"name": "bigquery-data-analytics",
33
"version": "0.1.7",
44
"description": "Connect, query, and generate data insights for BigQuery datasets and data.",
5-
"mcpServers": {
6-
"bigquery_data_analytics": {
7-
"command": "${extensionPath}${/}toolbox",
8-
"args": [
9-
"--tools-file",
10-
"${extensionPath}${/}tools.yaml",
11-
"--stdio"
12-
]
13-
}
14-
},
155
"contextFileName": "BIGQUERY.md",
166
"settings": [
177
{
@@ -25,4 +15,4 @@
2515
"envVar": "BIGQUERY_LOCATION"
2616
}
2717
]
28-
}
18+
}

skills/bigquery-analytics/SKILL.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
name: bigquery-analytics
3+
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.
4+
---
5+
6+
## Usage
7+
8+
All scripts can be executed using Node.js. Replace `<param_name>` and `<param_value>` with actual values.
9+
10+
**Bash:**
11+
`node <skill_dir>/scripts/<script_name>.js '{"<param_name>": "<param_value>"}'`
12+
13+
**PowerShell:**
14+
`node <skill_dir>/scripts/<script_name>.js '{\"<param_name>\": \"<param_value>\"}'`
15+
16+
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.
17+
18+
19+
## Scripts
20+
21+
22+
### analyze_contribution
23+
24+
Use this skill to analyze the contribution about changes to key metrics in multi-dimensional data.
25+
26+
#### Parameters
27+
28+
| Name | Type | Description | Required | Default |
29+
| :--- | :--- | :--- | :--- | :--- |
30+
| 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 | |
31+
| contribution_metric | string | The name of the column that contains the metric to analyze.
32+
Provides the expression to use to calculate the metric you are analyzing.
33+
To calculate a summable metric, the expression must be in the form SUM(metric_column_name),
34+
where metric_column_name is a numeric data type.
35+
36+
To calculate a summable ratio metric, the expression must be in the form
37+
SUM(numerator_metric_column_name)/SUM(denominator_metric_column_name),
38+
where numerator_metric_column_name and denominator_metric_column_name are numeric data types.
39+
40+
To calculate a summable by category metric, the expression must be in the form
41+
SUM(metric_sum_column_name)/COUNT(DISTINCT categorical_column_name). The summed column must be a numeric data type.
42+
The categorical column must have type BOOL, DATE, DATETIME, TIME, TIMESTAMP, STRING, or INT64. | Yes | |
43+
| is_test_col | string | The name of the column that identifies whether a row is in the test or control group. | Yes | |
44+
| dimension_id_cols | array | An array of column names that uniquely identify each dimension. | No | |
45+
| top_k_insights_by_apriori_support | integer | The number of top insights to return, ranked by apriori support. | No | `30` |
46+
| pruning_method | string | The method to use for pruning redundant insights. Can be 'NO_PRUNING' or 'PRUNE_REDUNDANT_INSIGHTS'. | No | `PRUNE_REDUNDANT_INSIGHTS` |
47+
48+
49+
---
50+
51+
### ask_data_insights
52+
53+
Use this skill to perform data analysis, get insights,
54+
or answer complex questions about the contents of specific
55+
BigQuery tables.
56+
57+
58+
#### Parameters
59+
60+
| Name | Type | Description | Required | Default |
61+
| :--- | :--- | :--- | :--- | :--- |
62+
| user_query_with_context | string | The user's question, potentially including conversation history and system instructions for context. | Yes | |
63+
| 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 | |
64+
65+
66+
---
67+
68+
### forecast
69+
70+
Use this skill to forecast time series data.
71+
72+
#### Parameters
73+
74+
| Name | Type | Description | Required | Default |
75+
| :--- | :--- | :--- | :--- | :--- |
76+
| history_data | string | The table id or the query of the history time series data. | Yes | |
77+
| timestamp_col | string | The name of the time series timestamp column. | Yes | |
78+
| data_col | string | The name of the time series data column. | Yes | |
79+
| id_cols | array | An array of the time series id column names. | No | `[]` |
80+
| horizon | integer | The number of forecasting steps. | No | `10` |
81+
82+
83+
---
84+
85+
### search_catalog
86+
87+
Use this skill to find tables, views, models, routines or connections.
88+
89+
#### Parameters
90+
91+
| Name | Type | Description | Required | Default |
92+
| :--- | :--- | :--- | :--- | :--- |
93+
| prompt | string | Prompt representing search intention. Do not rewrite the prompt. | Yes | |
94+
| datasetIds | array | Array of dataset IDs. | No | `[]` |
95+
| projectIds | array | Array of project IDs. | No | `[]` |
96+
| types | array | Array of data types to filter by. | No | `[]` |
97+
| pageSize | integer | Number of results in the search page. | No | `5` |
98+
99+
100+
---
101+
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env node
2+
3+
// Copyright 2026 Google LLC
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
const { spawn, execSync } = require('child_process');
18+
const path = require('path');
19+
const fs = require('fs');
20+
const os = require('os');
21+
22+
const toolName = "analyze_contribution";
23+
const configArgs = ["--prebuilt", "bigquery"];
24+
25+
const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [
26+
'BIGQUERY_LOCATION',
27+
'BIGQUERY_USE_CLIENT_OAUTH',
28+
'BIGQUERY_SCOPES',
29+
'BIGQUERY_MAX_QUERY_RESULT_ROWS',
30+
'BIGQUERY_IMPERSONATE_SERVICE_ACCOUNT',
31+
];
32+
33+
34+
function mergeEnvVars(env) {
35+
if (process.env.GEMINI_CLI === '1') {
36+
const envPath = path.resolve(__dirname, '../../../.env');
37+
if (fs.existsSync(envPath)) {
38+
const envContent = fs.readFileSync(envPath, 'utf-8');
39+
envContent.split('\n').forEach(line => {
40+
const trimmed = line.trim();
41+
if (trimmed && !trimmed.startsWith('#')) {
42+
const splitIdx = trimmed.indexOf('=');
43+
if (splitIdx !== -1) {
44+
const key = trimmed.slice(0, splitIdx).trim();
45+
let value = trimmed.slice(splitIdx + 1).trim();
46+
value = value.replace(/(^['"]|['"]$)/g, '');
47+
if (env[key] === undefined) {
48+
env[key] = value;
49+
}
50+
}
51+
}
52+
});
53+
}
54+
} else if (process.env.CLAUDECODE === '1') {
55+
const prefix = 'CLAUDE_PLUGIN_OPTION_';
56+
for (const key in process.env) {
57+
if (key.startsWith(prefix)) {
58+
env[key.substring(prefix.length)] = process.env[key];
59+
}
60+
}
61+
}
62+
}
63+
64+
function prepareEnvironment() {
65+
let env = { ...process.env };
66+
let userAgent = "skills";
67+
if (process.env.GEMINI_CLI === '1') {
68+
userAgent = "skills-geminicli";
69+
} else if (process.env.CLAUDECODE === '1') {
70+
userAgent = "skills-claudecode";
71+
} else if (process.env.CODEX_CI === '1') {
72+
userAgent = "skills-codex";
73+
}
74+
mergeEnvVars(env);
75+
76+
OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => {
77+
if (env[varName] === '') {
78+
delete env[varName];
79+
}
80+
});
81+
82+
83+
return { env, userAgent };
84+
}
85+
86+
function main() {
87+
const { env, userAgent } = prepareEnvironment();
88+
const args = process.argv.slice(2);
89+
90+
const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx';
91+
const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args;
92+
const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs];
93+
94+
const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env });
95+
96+
97+
child.on('close', (code) => {
98+
process.exit(code);
99+
});
100+
101+
child.on('error', (err) => {
102+
console.error("Error executing toolbox:", err);
103+
process.exit(1);
104+
});
105+
}
106+
107+
main();

0 commit comments

Comments
 (0)