From 1d3f1533190063ec7fc7006c6b4ce00b94e0d9ad Mon Sep 17 00:00:00 2001 From: Twisha Bansal Date: Wed, 15 Apr 2026 12:27:45 +0530 Subject: [PATCH] feat: Add support for skills --- .../workflows/skills-validate-fallback.yml | 37 ++++++ .github/workflows/skills-validate.yml | 56 ++++++++ gemini-extension.json | 18 --- skills/cloud-sql-sqlserver-admin/SKILL.md | 125 ++++++++++++++++++ .../scripts/create_database.js | 103 +++++++++++++++ .../scripts/create_instance.js | 103 +++++++++++++++ .../scripts/create_user.js | 103 +++++++++++++++ .../scripts/get_instance.js | 103 +++++++++++++++ .../scripts/list_databases.js | 103 +++++++++++++++ .../scripts/list_instances.js | 103 +++++++++++++++ .../scripts/wait_for_operation.js | 103 +++++++++++++++ skills/cloud-sql-sqlserver-data/SKILL.md | 48 +++++++ .../scripts/execute_sql.js | 103 +++++++++++++++ .../scripts/list_tables.js | 103 +++++++++++++++ skills/cloud-sql-sqlserver-lifecycle/SKILL.md | 113 ++++++++++++++++ .../scripts/clone_instance.js | 103 +++++++++++++++ .../scripts/create_backup.js | 103 +++++++++++++++ .../scripts/get_instance.js | 103 +++++++++++++++ .../scripts/list_instances.js | 103 +++++++++++++++ .../scripts/restore_backup.js | 103 +++++++++++++++ .../scripts/wait_for_operation.js | 103 +++++++++++++++ skills/cloud-sql-sqlserver-monitor/SKILL.md | 86 ++++++++++++ .../scripts/get_system_metrics.js | 103 +++++++++++++++ 23 files changed, 2113 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/skills-validate-fallback.yml create mode 100644 .github/workflows/skills-validate.yml create mode 100644 skills/cloud-sql-sqlserver-admin/SKILL.md create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/create_database.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/create_instance.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/create_user.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/get_instance.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/list_databases.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/list_instances.js create mode 100755 skills/cloud-sql-sqlserver-admin/scripts/wait_for_operation.js create mode 100644 skills/cloud-sql-sqlserver-data/SKILL.md create mode 100755 skills/cloud-sql-sqlserver-data/scripts/execute_sql.js create mode 100755 skills/cloud-sql-sqlserver-data/scripts/list_tables.js create mode 100644 skills/cloud-sql-sqlserver-lifecycle/SKILL.md create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/clone_instance.js create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/create_backup.js create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/get_instance.js create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/list_instances.js create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/restore_backup.js create mode 100755 skills/cloud-sql-sqlserver-lifecycle/scripts/wait_for_operation.js create mode 100644 skills/cloud-sql-sqlserver-monitor/SKILL.md create mode 100755 skills/cloud-sql-sqlserver-monitor/scripts/get_system_metrics.js diff --git a/.github/workflows/skills-validate-fallback.yml b/.github/workflows/skills-validate-fallback.yml new file mode 100644 index 0000000..4a72464 --- /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." \ No newline at end of file diff --git a/.github/workflows/skills-validate.yml b/.github/workflows/skills-validate.yml new file mode 100644 index 0000000..5ca49ac --- /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 \ No newline at end of file diff --git a/gemini-extension.json b/gemini-extension.json index 0729680..c1f8e9f 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -2,24 +2,6 @@ "name": "cloud-sql-sqlserver", "version": "0.1.8", "description": "Connect to Cloud SQL for SQL Server", - "mcpServers": { - "cloud_sql_sqlserver_admin": { - "command": "${extensionPath}${/}toolbox", - "args": [ - "--prebuilt", - "cloud-sql-mssql-admin", - "--stdio" - ] - }, - "cloud_sql_sqlserver": { - "command": "${extensionPath}${/}toolbox", - "args": [ - "--prebuilt", - "cloud-sql-mssql", - "--stdio" - ] - } - }, "contextFileName": "CLOUD-SQL-SQLSERVER.md", "settings": [ { diff --git a/skills/cloud-sql-sqlserver-admin/SKILL.md b/skills/cloud-sql-sqlserver-admin/SKILL.md new file mode 100644 index 0000000..5b967d1 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/SKILL.md @@ -0,0 +1,125 @@ +--- +name: cloud-sql-sqlserver-admin +description: Use these skills when you need to provision new Cloud SQL for SQL Server instances, create databases and users, clone existing environments, and monitor the progress of long-running operations. +--- + +## 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 + + +### create_database + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instance | string | The ID of the instance where the database will be created. | Yes | | +| name | string | The name for the new database. Must be unique within the instance. | Yes | | + + +--- + +### create_instance + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| name | string | The name of the instance | Yes | | +| databaseVersion | string | The database version for SQL Server. If not specified, defaults to SQLSERVER_2022_STANDARD. | No | `SQLSERVER_2022_STANDARD` | +| rootPassword | string | The root password for the instance | Yes | | +| editionPreset | string | The edition of the instance. Can be `Production` or `Development`. This determines the default machine type and availability. Defaults to `Development`. | No | `Development` | + + +--- + +### create_user + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instance | string | The ID of the instance where the user will be created. | Yes | | +| name | string | The name for the new user. Must be unique within the instance. | Yes | | +| password | string | A secure password for the new user. Not required for IAM users. | No | | +| iamUser | boolean | Set to true to create a Cloud IAM user. | Yes | | + + +--- + +### get_instance + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| projectId | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instanceId | string | The instance ID | Yes | | + + +--- + +### list_databases + +Lists all databases for a Cloud SQL instance. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instance | string | The instance ID | Yes | | + + +--- + +### list_instances + +Lists all type of Cloud SQL instances for a project. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | + + +--- + +### wait_for_operation + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| operation | string | The operation ID | Yes | | + + +--- + diff --git a/skills/cloud-sql-sqlserver-admin/scripts/create_database.js b/skills/cloud-sql-sqlserver-admin/scripts/create_database.js new file mode 100755 index 0000000..b3e6240 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/create_database.js @@ -0,0 +1,103 @@ +#!/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 = "create_database"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/create_instance.js b/skills/cloud-sql-sqlserver-admin/scripts/create_instance.js new file mode 100755 index 0000000..87e0db3 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/create_instance.js @@ -0,0 +1,103 @@ +#!/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 = "create_instance"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/create_user.js b/skills/cloud-sql-sqlserver-admin/scripts/create_user.js new file mode 100755 index 0000000..d953c67 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/create_user.js @@ -0,0 +1,103 @@ +#!/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 = "create_user"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/get_instance.js b/skills/cloud-sql-sqlserver-admin/scripts/get_instance.js new file mode 100755 index 0000000..1b0ae44 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/get_instance.js @@ -0,0 +1,103 @@ +#!/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_instance"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/list_databases.js b/skills/cloud-sql-sqlserver-admin/scripts/list_databases.js new file mode 100755 index 0000000..6f1390f --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/list_databases.js @@ -0,0 +1,103 @@ +#!/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_databases"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/list_instances.js b/skills/cloud-sql-sqlserver-admin/scripts/list_instances.js new file mode 100755 index 0000000..0d4e04c --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/list_instances.js @@ -0,0 +1,103 @@ +#!/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_instances"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-admin/scripts/wait_for_operation.js b/skills/cloud-sql-sqlserver-admin/scripts/wait_for_operation.js new file mode 100755 index 0000000..8d99bc1 --- /dev/null +++ b/skills/cloud-sql-sqlserver-admin/scripts/wait_for_operation.js @@ -0,0 +1,103 @@ +#!/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 = "wait_for_operation"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-data/SKILL.md b/skills/cloud-sql-sqlserver-data/SKILL.md new file mode 100644 index 0000000..efa4ce3 --- /dev/null +++ b/skills/cloud-sql-sqlserver-data/SKILL.md @@ -0,0 +1,48 @@ +--- +name: cloud-sql-sqlserver-data +description: Use these skills when you need to explore the database schema, execute SQL queries to interact with your data, and monitor system-level performance metrics using PromQL queries. +--- + +## 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 tool to execute SQL. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| sql | string | The sql to execute. | Yes | | + + +--- + +### list_tables + +Lists detailed schema information (object type, columns, constraints, indexes, triggers, comment) 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. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| table_names | string | Optional: A comma-separated list of table names. If empty, details for all tables will be listed. | No | `` | +| output_format | string | Optional: Use 'simple' for names only or 'detailed' for full info. | No | `detailed` | + + +--- + diff --git a/skills/cloud-sql-sqlserver-data/scripts/execute_sql.js b/skills/cloud-sql-sqlserver-data/scripts/execute_sql.js new file mode 100755 index 0000000..fc7d7c0 --- /dev/null +++ b/skills/cloud-sql-sqlserver-data/scripts/execute_sql.js @@ -0,0 +1,103 @@ +#!/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", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-data/scripts/list_tables.js b/skills/cloud-sql-sqlserver-data/scripts/list_tables.js new file mode 100755 index 0000000..b4e6280 --- /dev/null +++ b/skills/cloud-sql-sqlserver-data/scripts/list_tables.js @@ -0,0 +1,103 @@ +#!/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_tables"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/SKILL.md b/skills/cloud-sql-sqlserver-lifecycle/SKILL.md new file mode 100644 index 0000000..5742ed6 --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/SKILL.md @@ -0,0 +1,113 @@ +--- +name: cloud-sql-sqlserver-lifecycle +description: Use these skills when you need to manage the lifecycle and durability of your data, including creating backups, restoring from existing backups, and cloning instances for testing or migration. +--- + +## 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 + + +### clone_instance + +Clone an existing Cloud SQL instance into a new instance. The clone can be a direct copy of the source instance, or a point-in-time-recovery (PITR) clone from a specific timestamp. The call returns a Cloud SQL Operation object. Call wait_for_operation tool after this, make sure to use multiplier as 4 to poll the opertation status till it is marked DONE. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| sourceInstanceName | string | The name of the instance to be cloned. | Yes | | +| destinationInstanceName | string | The name of the new instance that will be created by cloning the source instance. | Yes | | +| pointInTime | string | The timestamp in RFC 3339 format to which the source instance should be cloned. | No | | +| preferredZone | string | The preferred zone for the new instance. | No | | +| preferredSecondaryZone | string | The preferred secondary zone for the new instance. | No | | + + +--- + +### create_backup + +Creates a backup on a Cloud SQL instance. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instance | string | Cloud SQL instance ID. This does not include the project ID. | Yes | | +| location | string | Location of the backup run. | No | | +| backup_description | string | The description of this backup run. | No | | + + +--- + +### get_instance + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| projectId | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| instanceId | string | The instance ID | Yes | | + + +--- + +### list_instances + +Lists all type of Cloud SQL instances for a project. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | + + +--- + +### restore_backup + +Restores a backup on a Cloud SQL instance. + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| target_project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| target_instance | string | Cloud SQL instance ID of the target instance. This does not include the project ID. | Yes | | +| backup_id | string | Identifier of the backup being restored. Can be a BackupRun ID, backup name, or BackupDR backup name. Use the full backup ID as provided, do not try to parse it | Yes | | +| source_project | string | GCP project ID of the instance that the backup belongs to. Only required if the backup_id is a BackupRun ID. | No | | +| source_instance | string | Cloud SQL instance ID of the instance that the backup belongs to. Only required if the backup_id is a BackupRun ID. | No | | + + +--- + +### wait_for_operation + + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| project | string | The GCP project ID. This is pre-configured; do not ask for it unless the user explicitly provides a different one. | No | | +| operation | string | The operation ID | Yes | | + + +--- + diff --git a/skills/cloud-sql-sqlserver-lifecycle/scripts/clone_instance.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/clone_instance.js new file mode 100755 index 0000000..6bf53ab --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/clone_instance.js @@ -0,0 +1,103 @@ +#!/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 = "clone_instance"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/scripts/create_backup.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/create_backup.js new file mode 100755 index 0000000..aa799e5 --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/create_backup.js @@ -0,0 +1,103 @@ +#!/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 = "create_backup"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/scripts/get_instance.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/get_instance.js new file mode 100755 index 0000000..1b0ae44 --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/get_instance.js @@ -0,0 +1,103 @@ +#!/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_instance"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/scripts/list_instances.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/list_instances.js new file mode 100755 index 0000000..0d4e04c --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/list_instances.js @@ -0,0 +1,103 @@ +#!/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_instances"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/scripts/restore_backup.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/restore_backup.js new file mode 100755 index 0000000..f773c09 --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/restore_backup.js @@ -0,0 +1,103 @@ +#!/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 = "restore_backup"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-lifecycle/scripts/wait_for_operation.js b/skills/cloud-sql-sqlserver-lifecycle/scripts/wait_for_operation.js new file mode 100755 index 0000000..8d99bc1 --- /dev/null +++ b/skills/cloud-sql-sqlserver-lifecycle/scripts/wait_for_operation.js @@ -0,0 +1,103 @@ +#!/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 = "wait_for_operation"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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/cloud-sql-sqlserver-monitor/SKILL.md b/skills/cloud-sql-sqlserver-monitor/SKILL.md new file mode 100644 index 0000000..e978b67 --- /dev/null +++ b/skills/cloud-sql-sqlserver-monitor/SKILL.md @@ -0,0 +1,86 @@ +--- +name: cloud-sql-sqlserver-monitor +description: Use these skills when you need to troubleshoot slow queries and analyze system-level PromQL metrics. +--- + +## 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 + + +### get_system_metrics + +Fetches system level cloudmonitoring data (timeseries metrics) for a SqlServer instance using a PromQL query. Take projectId and instanceId from the user for which the metrics timeseries data needs to be fetched. +To use this tool, you must provide the Google Cloud `projectId` and a PromQL `query`. + +Generate PromQL `query` for SqlServer system metrics. Use the provided metrics and rules to construct queries, Get the labels like `instance_id` from user intent. + +Defaults: +1. Interval: Use a default interval of `5m` for `_over_time` aggregation functions unless a different window is specified by the user. + +PromQL Query Examples: +1. Basic Time Series: `avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m])` +2. Top K: `topk(30, avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +3. Mean: `avg(avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +4. Minimum: `min(min_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +5. Maximum: `max(max_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +6. Sum: `sum(avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +7. Count streams: `count(avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` +8. Percentile with groupby on database_id: `quantile by ("database_id")(0.99,avg_over_time({"__name__"="cloudsql.googleapis.com/database/cpu/utilization","monitored_resource"="cloudsql_database","project_id"="my-projectId","database_id"="my-projectId:my-instanceId"}[5m]))` + +Available Metrics List: metricname. description. monitored resource. labels. database_id is actually the instance id and the format is `project_id:instance_id`. +1. `cloudsql.googleapis.com/database/cpu/utilization`: Current CPU utilization as a percentage of the reserved CPU. `cloudsql_database`. `database`, `project_id`, `database_id`. +2. `cloudsql.googleapis.com/database/memory/usage`: RAM usage in bytes, excluding buffer/cache. `cloudsql_database`. `database`, `project_id`, `database_id`. +3. `cloudsql.googleapis.com/database/memory/total_usage`: Total RAM usage in bytes, including buffer/cache. `cloudsql_database`. `database`, `project_id`, `database_id`. +4. `cloudsql.googleapis.com/database/disk/bytes_used`: Data utilization in bytes. `cloudsql_database`. `database`, `project_id`, `database_id`. +5. `cloudsql.googleapis.com/database/disk/quota`: Maximum data disk size in bytes. `cloudsql_database`. `database`, `project_id`, `database_id`. +6. `cloudsql.googleapis.com/database/disk/read_ops_count`: Delta count of data disk read IO operations. `cloudsql_database`. `database`, `project_id`, `database_id`. +7. `cloudsql.googleapis.com/database/disk/write_ops_count`: Delta count of data disk write IO operations. `cloudsql_database`. `database`, `project_id`, `database_id`. +8. `cloudsql.googleapis.com/database/network/received_bytes_count`: Delta count of bytes received through the network. `cloudsql_database`. `database`, `project_id`, `database_id`. +9. `cloudsql.googleapis.com/database/network/sent_bytes_count`: Delta count of bytes sent through the network. `cloudsql_database`. `destination`, `database`, `project_id`, `database_id`. +10. `cloudsql.googleapis.com/database/sqlserver/memory/buffer_cache_hit_ratio`: Current percentage of pages found in the buffer cache without reading from disk. `cloudsql_database`. `database`, `project_id`, `database_id`. +11. `cloudsql.googleapis.com/database/sqlserver/memory/memory_grants_pending`: Current number of processes waiting for a workspace memory grant. `cloudsql_database`. `database`, `project_id`, `database_id`. +12. `cloudsql.googleapis.com/database/sqlserver/memory/free_list_stall_count`: Total number of requests that waited for a free page. `cloudsql_database`. `database`, `project_id`, `database_id`. +13. `cloudsql.googleapis.com/database/swap/pages_swapped_in_count`: Total count of pages swapped in from disk since the system was booted. `cloudsql_database`. `database`, `project_id`, `database_id`. +14. `cloudsql.googleapis.com/database/swap/pages_swapped_out_count`: Total count of pages swapped out to disk since the system was booted. `cloudsql_database`. `database`, `project_id`, `database_id`. +15. `cloudsql.googleapis.com/database/sqlserver/memory/checkpoint_page_count`: Total number of pages flushed to disk by a checkpoint. `cloudsql_database`. `database`, `project_id`, `database_id`. +16. `cloudsql.googleapis.com/database/sqlserver/memory/lazy_write_count`: Total number of buffers written by the buffer manager's lazy writer. `cloudsql_database`. `database`, `project_id`, `database_id`. +17. `cloudsql.googleapis.com/database/sqlserver/memory/page_life_expectancy`: Current number of seconds a page will stay in the buffer pool. `cloudsql_database`. `database`, `project_id`, `database_id`. +18. `cloudsql.googleapis.com/database/sqlserver/memory/page_operation_count`: Total number of physical database page reads or writes. `cloudsql_database`. `operation`, `database`, `project_id`, `database_id`. +19. `cloudsql.googleapis.com/database/sqlserver/transactions/page_split_count`: Total number of page splits from overflowing index pages. `cloudsql_database`. `database`, `project_id`, `database_id`. +20. `cloudsql.googleapis.com/database/sqlserver/transactions/deadlock_count`: Total number of lock requests that resulted in a deadlock. `cloudsql_database`. `locked_resource`, `database`, `project_id`, `database_id`. +21. `cloudsql.googleapis.com/database/sqlserver/transactions/transaction_count`: Total number of transactions started. `cloudsql_database`. `database`, `project_id`, `database_id`. +22. `cloudsql.googleapis.com/database/sqlserver/transactions/batch_request_count`: Total number of Transact-SQL command batches received. `cloudsql_database`. `database`, `project_id`, `database_id`. +23. `cloudsql.googleapis.com/database/sqlserver/transactions/sql_compilation_count`: Total number of SQL compilations. `cloudsql_database`. `database`, `project_id`, `database_id`. +24. `cloudsql.googleapis.com/database/sqlserver/transactions/sql_recompilation_count`: Total number of SQL recompilations. `cloudsql_database`. `database`, `project_id`, `database_id`. +25. `cloudsql.googleapis.com/database/sqlserver/connections/processes_blocked`: Current number of blocked processes. `cloudsql_database`. `database`, `project_id`, `database_id`. +26. `cloudsql.googleapis.com/database/sqlserver/transactions/lock_wait_time`: Total time lock requests were waiting for locks. `cloudsql_database`. `locked_resource`, `database`, `project_id`, `database_id`. +27. `cloudsql.googleapis.com/database/sqlserver/transactions/lock_wait_count`: Total number of lock requests that required the caller to wait. `cloudsql_database`. `locked_resource`, `database`, `project_id`, `database_id`. +28. `cloudsql.googleapis.com/database/network/connections`: Number of connections to databases on the instance. `cloudsql_database`. `database`, `project_id`, `database_id`. +29. `cloudsql.googleapis.com/database/sqlserver/connections/login_attempt_count`: Total number of login attempts since the last server restart. `cloudsql_database`. `database`, `project_id`, `database_id`. +30. `cloudsql.googleapis.com/database/sqlserver/connections/logout_count`: Total number of logout operations since the last server restart. `cloudsql_database`. `database`, `project_id`, `database_id`. +31. `cloudsql.googleapis.com/database/sqlserver/connections/connection_reset_count`: Total number of logins started from the connection pool since the last server restart. `cloudsql_database`. `database`, `project_id`, `database_id`. +32. `cloudsql.googleapis.com/database/sqlserver/transactions/full_scan_count`: Total number of unrestricted full scans (base-table or full-index). `cloudsql_database`. `database`, `project_id`, `database_id`. + + +#### Parameters + +| Name | Type | Description | Required | Default | +| :--- | :--- | :--- | :--- | :--- | +| projectId | string | The Id of the Google Cloud project. | Yes | | +| query | string | The promql query to execute. | Yes | | + + +--- + diff --git a/skills/cloud-sql-sqlserver-monitor/scripts/get_system_metrics.js b/skills/cloud-sql-sqlserver-monitor/scripts/get_system_metrics.js new file mode 100755 index 0000000..68ac1d8 --- /dev/null +++ b/skills/cloud-sql-sqlserver-monitor/scripts/get_system_metrics.js @@ -0,0 +1,103 @@ +#!/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_system_metrics"; +const configArgs = ["--prebuilt", "cloud-sql-mssql"]; + +const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ + 'CLOUD_SQL_MSSQL_IP_TYPE', +]; + + +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();