Skip to content

Commit db0b873

Browse files
committed
feat: add support for skills and remove MCP server config
1 parent 57b0b56 commit db0b873

11 files changed

Lines changed: 910 additions & 10 deletions

File tree

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@v4
34+
35+
- name: Set up Python
36+
uses: actions/setup-python@v5
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: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@
22
"name": "spanner",
33
"version": "0.2.6",
44
"description": "Connect and interact with Spanner data using natural language.",
5-
"mcpServers": {
6-
"spanner": {
7-
"command": "${extensionPath}${/}toolbox",
8-
"args": [
9-
"--prebuilt",
10-
"spanner",
11-
"--stdio"
12-
]
13-
}
14-
},
155
"contextFileName": "SPANNER.md",
166
"settings": [
177
{

skills-gen/creds.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export SPANNER_PROJECT=toolbox-testing-438616
2+
export SPANNER_INSTANCE=spanner-testing
3+
export SPANNER_DATABASE=test_database

skills-gen/db_groups.md

Lines changed: 253 additions & 0 deletions
Large diffs are not rendered by default.

skills-gen/skill-support.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
You're a senior software engineer who's working to support skills in extensions for Google Data Cloud.
2+
3+
To support that create the following PRs in your repo. For each PR, first make the desired changes and then create a PR in the github repo. Please ensure that the PRs have good branch names as well as PR titles and descriptions. Directly create PRs in the repo. DO NOT CREATE ANY FORKS. All PRs should be created in a draft state.
4+
5+
# PR1: Add support for skills
6+
7+
1. Generate new skills using the latest toolbox binary (v1.1.0). Toolbox installation command:
8+
9+
```bash
10+
export VERSION=1.1.0
11+
curl -L -o toolbox2 https://storage.googleapis.com/mcp-toolbox-for-databases/v$VERSION/darwin/arm64/toolbox
12+
chmod +x toolbox2
13+
```
14+
15+
Then set the required env vars for configuration. You can
16+
find those in the configuration section in the readme file. Eg. https://github.com/gemini-cli-extensions/alloydb?tab=readme-ov-file#configuration.
17+
18+
Command example to generate skills for a toolset:
19+
20+
```bash
21+
./toolbox2 --prebuilt cloud-sql-postgres skills-generate --name "cloud-sql-postgres-health" --description "Use these skills when you need to audit database health, identify storage bloat, find invalid indexes, analyze table statistics, and manage maintenance configurations like autovacuum." --toolset=monitor --license-header "// Copyright 2026 Google LLC
22+
//
23+
// Licensed under the Apache License, Version 2.0 (the \"License\");
24+
// you may not use this file except in compliance with the License.
25+
// You may obtain a copy of the License at
26+
//
27+
// http://www.apache.org/licenses/LICENSE-2.0
28+
//
29+
// Unless required by applicable law or agreed to in writing, software
30+
// distributed under the License is distributed on an \"AS IS\" BASIS,
31+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32+
// See the License for the specific language governing permissions and
33+
// 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." --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."
34+
```
35+
36+
Write the commands for all tools in generate-skills-repo.sh script. Do not commit this file.
37+
38+
Remember to generate skills for all toolsets mentioned in https://github.com/googleapis/mcp-toolbox/tree/main/internal/prebuiltconfigs/tools for the source. Eg. AlloyDB Source: https://github.com/googleapis/mcp-toolbox/blob/main/internal/prebuiltconfigs/tools/alloydb-postgres.yaml.
39+
40+
Find list of sources and their tools grouping along with toolset descriptions [here](./db_groups.md).
41+
42+
After skills generation, cross check the generated skills from the [source yaml files](https://github.com/googleapis/mcp-toolbox/tree/main/internal/prebuiltconfigs/tools) and the [document](./db_groups.md) provided above.
43+
44+
Now, for all generated skills, replace skill descriptions from using tools -> skills.
45+
46+
2. Add skills validation workflow. Eg: https://github.com/gemini-cli-extensions/cloud-sql-postgresql/blob/main/.github/workflows/skills-validate.yml and https://github.com/gemini-cli-extensions/cloud-sql-postgresql/blob/main/.github/workflows/skills-validate-fallback.yml.
47+
48+
3. Remove the MCP servers from the gemini-extension.json file.
49+
50+
# PR2: Remove packaging workflow
51+
52+
1. Remove the github package and upload assets workflow: .github/workflows/package-and-upload-assets.yml.
53+
54+
# PR3: Add Claude code plugin config
55+
56+
1. Replicate this PR: https://github.com/gemini-cli-extensions/cloud-sql-postgresql/pull/137. Ensure that the plugin.json user config is consistent with the env vars in the gemini-extension.json file.
57+
58+
# PR4: Add Codex plugin config
59+
60+
1. Replicate this PR: https://github.com/gemini-cli-extensions/cloud-sql-postgresql/pull/138
61+
62+
# PR5: Auto update plugin versions using rp
63+
64+
1. Replicate this PR: https://github.com/gemini-cli-extensions/cloud-sql-postgresql/pull/155. Also ensure that "README.md" is present in extra-files section for release-please.
65+
66+
# PR6: Make skill docs changes:
67+
68+
1. Find the context file in gemini-extension.json, for eg. CLOUD-SQL-POSTGRESQL.md for the CLOUD SQL repo: https://github.com/gemini-cli-extensions/cloud-sql-postgresql and make changes analogous to what was done in https://github.com/gemini-cli-extensions/cloud-sql-postgresql/pull/109/.
69+
70+
2. Make changes to the README.md and DEVELOPER.md files. Use https://github.com/gemini-cli-extensions/cloud-sql-postgresql/blob/main/DEVELOPER.md and https://github.com/gemini-cli-extensions/cloud-sql-postgresql/blob/main/README.md on potential changes.
71+
72+
3. Check for any "tools" mention in the repository that needs to be updated to skills.
73+
74+
4. Ensure that you have covered updates on the README on how to use the extensions with Gemini CLI, Claude Code, Codex and Antigravity. Be very through with looking into this: https://github.com/gemini-cli-extensions/cloud-sql-postgresql/blob/main/README.md and making updates. Notice that this readme has a table of contents and collapsible sections for claude code etc.

skills/spanner-data/SKILL.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
name: spanner-data
3+
description: Use these skills when you need to explore the database structure, discover schema objects like tables and graphs, and execute custom SQL queries to interact with your data.
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+
### execute_sql
23+
24+
Use this tool to execute DML SQL. Please use the googlesql interface for Spanner.
25+
26+
#### Parameters
27+
28+
| Name | Type | Description | Required | Default |
29+
| :--- | :--- | :--- | :--- | :--- |
30+
| sql | string | The sql to execute. | Yes | |
31+
32+
33+
---
34+
35+
### execute_sql_dql
36+
37+
Use this tool to execute DQL SQL. Please use the googlesql interface for Spanner.
38+
39+
#### Parameters
40+
41+
| Name | Type | Description | Required | Default |
42+
| :--- | :--- | :--- | :--- | :--- |
43+
| sql | string | The sql to execute. | Yes | |
44+
45+
46+
---
47+
48+
### list_graphs
49+
50+
Lists detailed graph schema information (node tables, edge tables, labels and property declarations) as JSON for user-created graphs. Filters by a comma-separated list of graph names. If names are omitted, lists all graphs. The output can be 'simple' (graph names only) or 'detailed' (full schema).
51+
52+
#### Parameters
53+
54+
| Name | Type | Description | Required | Default |
55+
| :--- | :--- | :--- | :--- | :--- |
56+
| graph_names | string | Optional: A comma-separated list of graph names. If empty, details for all graphs in user-accessible schemas will be listed. | No | `` |
57+
| output_format | string | Optional: Use 'simple' to return graph names only or use 'detailed' to return the full information schema. | No | `detailed` |
58+
59+
60+
---
61+
62+
### list_tables
63+
64+
Lists detailed schema information (object type, columns, constraints, indexes) as JSON for user-created tables (ordinary or partitioned). Filters by a comma-separated list of names. If names are omitted, lists all tables in user schemas. The output can be 'simple' (table names only) or 'detailed' (full schema).
65+
66+
#### Parameters
67+
68+
| Name | Type | Description | Required | Default |
69+
| :--- | :--- | :--- | :--- | :--- |
70+
| table_names | string | Optional: A comma-separated list of table names. If empty, details for all tables in user-accessible schemas will be listed. | No | `` |
71+
| output_format | string | Optional: Use 'simple' to return table names only or use 'detailed' to return the full information schema. | No | `detailed` |
72+
73+
74+
---
75+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 = "execute_sql";
23+
const configArgs = ["--prebuilt", "spanner"];
24+
25+
const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [
26+
'SPANNER_DIALECT',
27+
];
28+
29+
30+
function mergeEnvVars(env) {
31+
if (process.env.GEMINI_CLI === '1') {
32+
const envPath = path.resolve(__dirname, '../../../.env');
33+
if (fs.existsSync(envPath)) {
34+
const envContent = fs.readFileSync(envPath, 'utf-8');
35+
envContent.split('\n').forEach(line => {
36+
const trimmed = line.trim();
37+
if (trimmed && !trimmed.startsWith('#')) {
38+
const splitIdx = trimmed.indexOf('=');
39+
if (splitIdx !== -1) {
40+
const key = trimmed.slice(0, splitIdx).trim();
41+
let value = trimmed.slice(splitIdx + 1).trim();
42+
value = value.replace(/(^['"]|['"]$)/g, '');
43+
if (env[key] === undefined) {
44+
env[key] = value;
45+
}
46+
}
47+
}
48+
});
49+
}
50+
} else if (process.env.CLAUDECODE === '1') {
51+
const prefix = 'CLAUDE_PLUGIN_OPTION_';
52+
for (const key in process.env) {
53+
if (key.startsWith(prefix)) {
54+
env[key.substring(prefix.length)] = process.env[key];
55+
}
56+
}
57+
}
58+
}
59+
60+
function prepareEnvironment() {
61+
let env = { ...process.env };
62+
let userAgent = "skills";
63+
if (process.env.GEMINI_CLI === '1') {
64+
userAgent = "skills-geminicli";
65+
} else if (process.env.CLAUDECODE === '1') {
66+
userAgent = "skills-claudecode";
67+
} else if (process.env.CODEX_CI === '1') {
68+
userAgent = "skills-codex";
69+
}
70+
mergeEnvVars(env);
71+
72+
OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => {
73+
if (env[varName] === '') {
74+
delete env[varName];
75+
}
76+
});
77+
78+
79+
return { env, userAgent };
80+
}
81+
82+
function main() {
83+
const { env, userAgent } = prepareEnvironment();
84+
const args = process.argv.slice(2);
85+
86+
const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx';
87+
const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args;
88+
const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs];
89+
90+
const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env });
91+
92+
93+
child.on('close', (code) => {
94+
process.exit(code);
95+
});
96+
97+
child.on('error', (err) => {
98+
console.error("Error executing toolbox:", err);
99+
process.exit(1);
100+
});
101+
}
102+
103+
main();

0 commit comments

Comments
 (0)