Snowflake Setup
Manage Snowflake connection profiles stored in ~/.claude/snowflake-profiles.json.
Ask one question at a time for dependent decisions (credential flows are mostly sequential). Batch independent questions when possible — e.g. profile name + connection method can be collected together.
Entry Point
Manage Snowflake connection profiles — add, list, update, delete, or test a profile.
Read ~/.claude/snowflake-profiles.json.
If no profiles file or empty profiles array: go directly to Add.
If profiles exist: show the menu.
Snowflake Profiles
1. {name} — {method_label} — {account_or_connection}
2. {name} — {method_label} — {account_or_connection}
...
L List profiles (full details of all profiles)
A Add a new profile
U Update a profile
D Delete a profile
T Test a profile
Q Quit
Enter L / A / U / D / T / Q:
For method_label display: method: python + auth: key_pair → python / key pair, method: python + auth: password → python / password, method: cli → Snowflake CLI.
For account_or_connection: show account for python profiles, cli_connection for cli profiles.
L — List
Show each profile with full details. For each profile:
Python connector:
Profile: {name}
Method: Python connector
Account: {account}
Username: {username}
Auth: {key pair | password}
Warehouse: {default_warehouse}
Role: {default_role}
{if password: Env var: {password_env} | Status: {SET | NOT SET}}
{if key_pair: Key file: ~/.ssh/snowflake_key.p8 | Exists: {yes | no}}
Snowflake CLI:
Profile: {name}
Method: Snowflake CLI
Connection: {cli_connection}
Warehouse: {default_warehouse}
Role: {default_role}
To check password env var status: read os.environ.get("{password_env}", "") — non-empty = SET.
To check key file: test whether ~/.ssh/snowflake_key.p8 exists.
After displaying all profiles, return to the menu.
A — Add
How should Claude connect to Snowflake?
1 Python connector — no extra tools needed; works with key pair or password
2 Snowflake CLI — uses your existing `snow` CLI and its config file
Not sure? Choose 1 — fewest dependencies.
Enter 1 or 2:
Path A — Python Connector
A1 — Collect Account Details
Ask one at a time:
Your Snowflake account identifier
(from your URL: https://<account-identifier>.snowflakecomputing.com):
Store as {account}.
Your Snowflake username:
Store as {username}.
Default warehouse (e.g. MY_WAREHOUSE):
Store as {warehouse}.
Default role (e.g. MY_ROLE):
Store as {role}.
Profile name for this connection: [Production]
Store as {profile_name}. Default to Production.
A2 — Choose Auth Method
Auth method:
1 Key pair — generates a key file; more secure (recommended)
2 Password — simpler setup
Enter 1 or 2:
A3a — Key Pair Auth
Check whether ~/.ssh/snowflake_key.p8 already exists.
If it exists:
Found an existing key at ~/.ssh/snowflake_key.p8.
Use it for this profile? (Y/n):
If yes: use it and skip key generation.
If it doesn't exist (or user declines):
Write to /tmp/sf_keygen.py:
import subprocess, os, sys
key_path = os.path.expanduser("~/.ssh/snowflake_key.p8")
pub_path = os.path.expanduser("~/.ssh/snowflake_key.pub")
os.makedirs(os.path.expanduser("~/.ssh"), exist_ok=True)
r1 = subprocess.run(["openssl", "genrsa", "2048"], capture_output=True)
if r1.returncode != 0:
print(f"Key generation failed: {r1.stderr.decode()}")
sys.exit(1)
r2 = subprocess.run(
["openssl", "pkcs8", "-topk8", "-inform", "PEM", "-out", key_path, "-nocrypt"],
input=r1.stdout, capture_output=True
)
if r2.returncode != 0:
print(f"PKCS8 conversion failed: {r2.stderr.decode()}")
sys.exit(1)
os.chmod(key_path, 0o600)
r3 = subprocess.run(["openssl", "rsa", "-in", key_path, "-pubout", "-out", pub_path], capture_output=True)
if r3.returncode != 0:
print(f"Public key extraction failed: {r3.stderr.decode()}")
sys.exit(1)
with open(pub_path) as f:
print(f.read())
Run: python3 /tmp/sf_keygen.py
Remove: rm -f /tmp/sf_keygen.py
Then show the user:
Key pair generated at ~/.ssh/snowflake_key.p8
Run this SQL in Snowsight to assign the public key to your user
(paste the lines between the -----BEGIN and -----END----- markers):
ALTER USER {username} SET RSA_PUBLIC_KEY='<public key contents here>';
Let me know when you've done that.
Wait for confirmation.
Save the profile:
ts profiles add \
--platform snowflake \
--name "{profile_name}" \
--auth-type key_pair \
--field account={account} \
--field username={username} \
--field private_key_path=~/.ssh/snowflake_key.p8 \
--field default_warehouse={warehouse} \
--field default_role={role}
Key pair auth does not use a keychain entry or env var — the key file is the credential.
A3b — Password Auth
Save profile and derive credentials:
ts profiles add \
--platform snowflake \
--name "{profile_name}" \
--auth-type password \
--field account={account} \
--field username={username} \
--field default_warehouse={warehouse} \
--field default_role={role}
Parse the JSON output. It contains keychain_store_commands, keychain_verify_commands,
zshenv_line, and windows_env_commands.
Store credential — detect platform first (python -c "import platform; print(platform.system())"),
then show the user the keychain store command for their platform from the
keychain_store_commands in the output, replacing VALUE with YOUR_PASSWORD_HERE.
Tell them to run it in their own terminal so the password never enters the conversation.
After the user confirms, show the verify command from keychain_verify_commands.
Stop if verification fails — do not proceed without a confirmed credential write.
Update shell profile
macOS / Linux: Read ~/.zshenv, upsert the zshenv_line from the output
(replace an existing line for the same env var, or append if not present), write back.
Tell the user to run source ~/.zshenv and wait for confirmation.
Windows: Show the windows_env_commands from the output for the user to run in
PowerShell. Note: on Windows this step is optional — the connector reads from Windows
Credential Manager via keyring at runtime.
A4 — Test Python Connector
Write to /tmp/sf_verify.py:
import os, sys
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import snowflake.connector
auth = "{auth}"
account = "{account}"
username = "{username}"
warehouse = "{warehouse}"
role = "{role}"
try:
if auth == "key_pair":
key_path = os.path.expanduser("~/.ssh/snowflake_key.p8")
with open(key_path, "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None, backend=default_backend())
private_key_bytes = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
conn = snowflake.connector.connect(account=account, user=username, private_key=private_key_bytes, role=role, warehouse=warehouse)
else:
env_var = "{env_var}"
password = os.environ.get(env_var, "")
if not password:
print(f"ERROR: {env_var} is empty — run 'source ~/.zshenv' first.")
sys.exit(1)
conn = snowflake.connector.connect(account=account, user=username, password=password, role=role, warehouse=warehouse)
cur = conn.cursor()
cur.execute("SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_WAREHOUSE()")
row = cur.fetchone()
print(f"User: {row[0]}")
print(f"Role: {row[1]}")
print(f"Warehouse: {row[2]}")
conn.close()
except Exception as e:
print(f"ERROR: {e}")
sys.exit(1)
macOS / Linux: python3 /tmp/sf_verify.py 2>/dev/null
Windows: python /tmp/sf_verify.py 2>/dev/null
Remove: rm -f /tmp/sf_verify.py (macOS/Linux) or del /tmp/sf_verify.py (Windows)
Path B — Snowflake CLI
B1 — Check CLI is Installed
Run: snow --version 2>&1
If not found (exit code 127), search for the binary:
find /usr/local/bin /opt/homebrew/bin /usr/bin ~/.local/bin ~/Library/Python -name "snow" 2>/dev/null | head -5
If found at an alternate path, store it as {snow_cmd} (e.g. ~/Library/Python/3.9/bin/snow) and use that path in all subsequent snow commands. Confirm to the user:
Found snow at {snow_cmd} (version X.Y.Z).
If not found anywhere:
Snowflake CLI (snow) is not installed.
Install it from:
https://docs.snowflake.com/en/developer-guide/snowflake-cli/installation/installation
Once installed, run `snow --version` to verify, then re-run /ts-profile-snowflake.
Stop here.
B2 — Choose or Create a Connection
Run: {snow_cmd} connection list 2>&1
Parse the connection names from the output and display them numbered:
Available connections:
1 {connection_name_1}
2 {connection_name_2}
...
N Create a new connection
Enter a number:
If existing connection selected (1…N-1): store the corresponding name as {cli_connection}. Skip to B3.
If N (new): ask for connection name — that becomes {cli_connection}. Skip to new-connection flow below.
If 'new': ask for connection name, account identifier, username, default role, default warehouse, and auth method (key pair path or browser SSO). Write the config block to ~/.snowflake/config.toml, show it to the user, and confirm before appending.
B3 — Collect Profile Details
Profile name for this connection: [Production]
Then ask:
Override the CLI connection's warehouse and role? (y/N):
If no (default): set {warehouse} and {role} to "" (empty — CLI connection defaults apply).
If yes: ask:
Default warehouse:
Default role:
Store as {profile_name}, {warehouse}, {role}.
B4 — Test CLI Connection
Run: {snow_cmd} connection test -c {cli_connection} 2>&1
If the test fails with No such file or directory on a key path:
Snow CLI 2.8.2 stores connections in ~/.snowflake/connections.toml (separate from config.toml) and does NOT expand ~ in private_key_path — it joins the path literally with the CWD. Fix by replacing ~ with the absolute home path in connections.toml:
private_key_path = "/Users/username/.ssh/snowflake_private_key.p8"
Read ~/.snowflake/connections.toml, update the private_key_path for the relevant connection, write it back, then re-run the test.
Show output. If it still fails, stop and ask the user to fix the CLI config before continuing.
B5 — Save Profile
ts profiles add \
--platform snowflake \
--name "{profile_name}" \
--auth-type cli \
--field cli_connection={cli_connection} \
--field snow_cmd={snow_cmd} \
--field default_warehouse={warehouse} \
--field default_role={role}
CLI auth does not use a keychain entry or env var.
Confirm
On success:
Snowflake profile '{profile_name}' configured and verified.
Return to menu (or exit if this was the first-run flow).
U — Update
Show numbered profile list and ask:
Which profile would you like to update? (enter number):
Then show what can be changed. Options vary by profile type:
Python connector (key pair):
What would you like to update?
1 Account identifier
2 Username
3 Warehouse
4 Role
5 Change auth method (switch to password)
Enter 1–5:
Python connector (password):
What would you like to update?
1 Account identifier
2 Username
3 Warehouse
4 Role
5 Refresh password (update stored value)
6 Change auth method (switch to key pair)
Enter 1–6:
Snowflake CLI:
What would you like to update?
1 CLI connection name
2 Warehouse
3 Role
Enter 1–3:
U — Simple Field Updates (Account, Warehouse, Role, CLI Connection)
New {field}: [{current_value}]
Run:
ts profiles update --platform snowflake --name "{profile_name}" --field {field_key}={new_value}
Confirm: {Field} updated.
U — Update Username (Password Profiles)
New username: [{current_username}]
If username changes on a password profile, migrate the stored credential:
macOS:
import subprocess
r = subprocess.run(["security", "find-generic-password", "-s", "{keychain_service}", "-a", "{old_username}", "-w"], capture_output=True, text=True)
credential = r.stdout.strip()
subprocess.run(["security", "delete-generic-password", "-s", "{keychain_service}", "-a", "{old_username}"], capture_output=True)
subprocess.run(["security", "add-generic-password", "-s", "{keychain_service}", "-a", "{new_username}", "-w", credential], capture_output=True)
Windows / Linux:
import keyring
credential = keyring.get_password("{keychain_service}", "{old_username}")
if credential:
keyring.delete_password("{keychain_service}", "{old_username}")
keyring.set_password("{keychain_service}", "{new_username}", credential)
Where {keychain_service} is snowflake-{slug} (slug derived from the profile name).
Then update the profile:
ts profiles update --platform snowflake --name "{profile_name}" --field username={new_username}
Also update the export line in ~/.zshenv to reference the new username.
Confirm: Username updated.
U — Refresh Password
Show the profile's auth details, then detect platform (platform.system()) and
ask the user to run in their own terminal.
The keychain service is snowflake-{slug} and the account is the profile's username.
macOS (Darwin):
Run this in your terminal to update the password:
security delete-generic-password -s "{keychain_service}" -a "{username}"
security add-generic-password \
-s "{keychain_service}" \
-a "{username}" \
-w "YOUR_NEW_PASSWORD_HERE"
Let me know when done.
Windows (PowerShell):
Run this in PowerShell to update the password:
python -c "import keyring; keyring.delete_password('{keychain_service}', '{username}')"
python -c "import keyring; keyring.set_password('{keychain_service}', '{username}', 'YOUR_NEW_PASSWORD_HERE')"
Let me know when done.
Linux:
Run this in your terminal to update the password:
python3 -c "import keyring; keyring.delete_password('{keychain_service}', '{username}')"
python3 -c "import keyring; keyring.set_password('{keychain_service}', '{username}', 'YOUR_NEW_PASSWORD_HERE')"
Let me know when done.
After confirmation, verify using the platform-specific check from the Add flow.
No profile JSON or shell profile changes needed (env var name stays the same).
macOS / Linux: Tell the user:
Password updated. Run this in your terminal to apply:
source ~/.zshenv
Windows: Password updated in Windows Credential Manager. Restart your terminal for the change to take effect (or it will be read directly from the credential store at next use).
U — Change Auth Method
Run the full auth setup section of Add for the chosen method:
- Key pair: generate or reuse key → assign public key in Snowsight →
ts profiles addwith--auth-type key_pair(replaces the existing profile; removepassword_env) - Password:
ts profiles addwith--auth-type password→ keychain → ~/.zshenv (replaces the existing profile; removeprivate_key_path)
Clean up the old auth:
- Switching to key pair: delete Keychain entry, remove export line from
~/.zshenv - Switching to password: no key file cleanup (key may be shared across profiles)
D — Delete
Show numbered profile list and ask:
Which profile would you like to delete? (enter number):
Confirm:
Delete profile '{name}'?
This will remove it from the profile file{, the macOS Keychain, and ~/.zshenv | (no Keychain or env var — key pair auth)}.
Y / N:
If confirmed:
- Remove profile:
ts profiles remove --platform snowflake --name "{profile_name}"
Parse the JSON output for keychain_service and env_var_to_remove.
If password auth — remove credential store entry:
macOS:
security delete-generic-password -s "{keychain_service}" -a "{username}"Windows / Linux:
import keyring try: keyring.delete_password("{keychain_service}", "{username}") except Exception: passIf not found, continue silently.
If password auth — remove export line from shell profile (macOS/Linux only) — read
~/.zshenv(or~/.bashrc), filter out any line that exports{env_var_to_remove}, write back. Skip on Windows.Tell the user:
Profile '{name}' deleted.
If password auth on macOS / Linux, add:
Run this in your terminal to apply the shell profile change:
source ~/.zshenv
If password auth on Windows: no shell profile reload needed. If key pair auth, add:
Note: the key file ~/.ssh/snowflake_key.p8 was not removed — it may be used by
other profiles or tools. Delete it manually if no longer needed.
T — Test
Show numbered profile list (if more than one) and ask which to test. If only one, confirm and test it directly.
Python connector:
Write to /tmp/sf_verify.py (same script as A4, with values filled in for the selected profile).
macOS / Linux: python3 /tmp/sf_verify.py 2>/dev/null
Windows: python /tmp/sf_verify.py 2>/dev/null
Remove: rm -f /tmp/sf_verify.py (macOS/Linux) or del /tmp/sf_verify.py (Windows)
Show the result as a table (User / Role / Warehouse).
On success: Profile '{name}' — connection verified. Return to menu.
Snowflake CLI:
Run: {snow_cmd} connection test -c {cli_connection} 2>&1
If that succeeds, run a live SQL query to confirm real query execution:
{snow_cmd} sql -c {cli_connection} -q "SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_WAREHOUSE(), CURRENT_DATABASE()" 2>&1
Show the result as a table.
On success: Profile '{name}' — connection verified. Return to menu.
Error Handling
| Symptom | Action |
|---|---|
snow not found |
Direct user to install CLI; stop |
| Key generation fails | Show openssl error; check openssl is installed (openssl version) |
ALTER USER not confirmed |
Wait; remind user the key pair won't work until this step is done |
| Credential write fails | macOS: check the login keychain is unlocked. Windows: ensure keyring is installed (pip install keyring). Linux: ensure a Secret Service backend is running (pip install keyring secretstorage). |
| Password env var empty after source | macOS/Linux: remind user to run source ~/.zshenv in a real terminal (not with !). Windows: env var is optional — connector reads from Credential Manager via keyring. |
| Snowflake 250001 / auth error | Check account identifier, username, role, warehouse are all correct |
snowflake.connector not found |
pip install snowflake-connector-python cryptography |
Technical Reference — For Use by Other Skills
Python Connector — Connection Code
Key pair:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import os, snowflake.connector
key_path = os.path.expanduser(profile['private_key_path'])
passphrase_env = profile.get('private_key_passphrase_env', '')
passphrase_bytes = os.environ.get(passphrase_env).encode() if passphrase_env else None
with open(key_path, 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(), password=passphrase_bytes, backend=default_backend()
)
private_key_bytes = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
conn = snowflake.connector.connect(
account=profile['account'], user=profile['username'],
private_key=private_key_bytes, role=role, warehouse=warehouse,
)
Password:
conn = snowflake.connector.connect(
account=profile['account'], user=profile['username'],
password=os.environ.get(profile['password_env']),
role=role, warehouse=warehouse,
)
SQL Execution
Python connector:
cur = conn.cursor()
cur.execute(f"CALL SYSTEM$CREATE_SEMANTIC_VIEW_FROM_YAML('{db}.{schema}', $${yaml}$$, TRUE)")
print(cur.fetchone())
conn.close()
Snowflake CLI — use a temp SQL file to avoid shell-escaping issues with dollar-quotes:
import subprocess
sql = f"""USE ROLE {role};
USE DATABASE {db};
USE SCHEMA {schema};
CALL SYSTEM$CREATE_SEMANTIC_VIEW_FROM_YAML('{db}.{schema}', $${yaml}$$, TRUE);
"""
with open('/tmp/sf_query.sql', 'w') as f:
f.write(sql)
result = subprocess.run(
[profile['snow_cmd'], 'sql', '-c', cli_connection, '--format', 'json', '-f', '/tmp/sf_query.sql'],
capture_output=True, text=True
)
print(result.stdout)
if result.returncode != 0:
print(result.stderr)
Remove TRUE for the actual CREATE (not dry-run). Cleanup: rm -f /tmp/sf_query.sql
SHOW Commands — Case-Sensitivity Detection
Python connector:
cur.execute(f"SHOW SCHEMAS IN DATABASE {db}")
cs_schemas = {r[1] for r in cur.fetchall() if r[1] != r[1].upper()}
schema_ref = f'"{schema}"' if schema in cs_schemas else schema
cur.execute(f'SHOW COLUMNS IN TABLE {db}.{schema_ref}."{phys_table}"')
cs_columns = {r[2] for r in cur.fetchall() if r[2] != r[2].upper()}
Snowflake CLI:
import subprocess, json
def snow_json(cli_connection, query, snow_cmd='snow'):
r = subprocess.run(
[snow_cmd, 'sql', '-c', cli_connection, '--format', 'json', '-q', query],
capture_output=True, text=True
)
return json.loads(r.stdout)
rows = snow_json(cli_connection, f"SHOW SCHEMAS IN DATABASE {db}")
cs_schemas = {r['name'] for r in rows if r['name'] != r['name'].upper()}
schema_ref = f'"{schema}"' if schema in cs_schemas else schema
rows = snow_json(cli_connection, f'SHOW COLUMNS IN TABLE {db}.{schema_ref}."{phys_table}"')
cs_columns = {r['column_name'] for r in rows if r['column_name'] != r['column_name'].upper()}
Lowercase in SHOW output = case-sensitive identifier = must be quoted in SQL.
Notes on SYSTEM$CREATE_SEMANTIC_VIEW_FROM_YAML
- Use
CALL, notSELECT - First argument: fully-qualified target schema as a string:
'DATABASE.SCHEMA' - Second argument: YAML in
$$dollar-quotes - Third argument
TRUE: dry-run (validates without creating) — always run first - Remove the third argument (or pass
FALSE) for the actual CREATE
Changelog
| Version | Date | Summary |
|---|---|---|
| 1.1.1 | 2026-07-22 | Relax prompt-batching: credential flows are mostly sequential, but independent inputs can now be batched (BL-074) |
| 1.1.0 | 2026-07-13 | Adopt ts profiles add/update/remove CLI commands — replaces hand-coded slug derivation, keychain commands, env var naming, and profile JSON I/O |
| 1.0.1 | 2026-04-24 | Add one-line context before menu |
| 1.0.0 | 2026-04-24 | Initial versioned release |