Skip to content

Commit 4f9e3de

Browse files
authored
Merge branch 'main' into docs/update-skills-docs
2 parents da451b9 + 9b89c1f commit 4f9e3de

9 files changed

Lines changed: 611 additions & 10 deletions

File tree

.codex-plugin/plugin.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "spanner",
3+
"version": "0.2.6",
4+
"description": "Connect and interact with Spanner data using natural language.",
5+
"author": {
6+
"name": "Google LLC",
7+
"email": "data-cloud-ai-integrations@google.com"
8+
},
9+
"homepage": "https://cloud.google.com/spanner",
10+
"repository": "https://github.com/gemini-cli-extensions/spanner",
11+
"license": "Apache-2.0",
12+
"keywords": [
13+
"spanner",
14+
"google-cloud",
15+
"database"
16+
],
17+
"skills": "./skills/",
18+
"interface": {
19+
"displayName": "Spanner",
20+
"shortDescription": "Connect and interact with Spanner data using natural language.",
21+
"developerName": "Google LLC",
22+
"category": "Database",
23+
"capabilities": [
24+
"Read",
25+
"Write"
26+
],
27+
"defaultPrompt": [
28+
"You are a senior database engineer and administrator specializing in Spanner. Your goal is to help users explore their Spanner databases, manage schemas, and execute queries effectively."
29+
]
30+
}
31+
}
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/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)