agentsclimarketplace

Mac secrets

Skill Vasallo94/skill-issue/skills/general/mac-secrets

A curated marketplace of open Agent Skills (SKILL.md) — the real skills I use day to day, compatible with the open skills ecosystem.

Install
npx -y skills add Vasallo94/skill-issue --skill mac-secrets

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Securely store and manage secrets (API tokens, PATs, passwords, credentials) in macOS Keychain for local development tools — MCP servers, CLI tools, wrapper scripts, .env files, and more. Use this skill whenever the user asks to "secure a token", "store a secret", "set up keychain", "remove plaintext credentials", "secure my .env", or mentions wanting to avoid secrets in plaintext on their Mac. Also trigger when discussing MCP server configuration that involves tokens or credentials on macOS, or when the user asks how to safely manage API keys locally. Trigger PROACTIVELY when you detect a secret being passed as plaintext in a command, env var, or config file — don't wait for the user to ask.

SKILL.md

8.6 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it

mac-secrets

Migrate secrets from plaintext files into macOS Keychain and wire up tooling to read them at runtime. This is a rigid process skill — follow the steps in order.

Why macOS Keychain

macOS Keychain encrypts secrets with the user's login session key. Secrets are only accessible when the session is unlocked and never sit in plaintext on disk. This is the native macOS equivalent of tools like pass, 1password-cli, or vault — no extra dependencies required.

The two key commands:

  • Store: security add-generic-password -s '<service>' -a '<account>' -w '<secret>' -U
  • Read: security find-generic-password -s '<service>' -a '<account>' -w

The -U flag updates the entry if it already exists (upsert behavior), though on some macOS versions it may fail — see Step 2 for workaround.

How Keychain entries are organized

Each entry has three fields — think of them as a filing system:

FieldPurposeAnalogy
-s (service)What the secret is — a label describing the credential typeThe folder label
-a (account)Who uses it — which tool or scope needs this secretThe drawer it goes in
-w (password)The actual secret value — this is the only sensitive partThe document inside

-s and -a are just names you choose for finding the secret later. The secret itself goes in -w.

Naming convention

Scope-s (service)-a (account)Example
Cross-project (used by Claude Code itself, glab, gh, etc.)<provider>-<type>claude-code-s 'gitlab-pat' -a 'claude-code'
Project-specific (only used by one project's MCP/tool)<provider>-<type><project-dir-name>-s 'confluence-pat' -a 'wiki-lda-mcp'

Use claude-code as account for tokens that work across repos (GitLab PAT, GitHub token, OpenAI key). Use the project directory name for tokens scoped to a single project.

Process

Step 1: Identify the secret

Determine (ask the user or infer from context):

FieldWhat it isExample
Secret valueThe actual token/passwordglpat-abc123...
Service nameDescriptive label for Keychain (kebab-case)gitlab-pat, confluence-pat, openai-api-key
Account nameclaude-code if cross-project, project dir name if scopedclaude-code, wiki-lda-mcp

Step 2: Store in Keychain

IMPORTANT: Never run the secret as a CLI argument in Claude Code's shell — it would appear in the conversation. Instead, tell the user to run the store command in a separate terminal.

Give the user this command to run in another terminal:

security add-generic-password -s '<service>' -a '<account>' -w

The -w flag without a value prompts interactively for the password — the secret never appears in shell history or on screen.

If -U (upsert) fails with "item already exists", tell the user to delete first:

security delete-generic-password -s '<service>' -a '<account>'
security add-generic-password -s '<service>' -a '<account>' -w

Do NOT use read -s -p — it does not work in zsh (-p means coprocess, not prompt).

Once the user confirms, verify from Claude Code's shell:

security find-generic-password -s '<service>' -a '<account>' -w | head -c 10 && echo "...(ok)"

Step 3: Create a wrapper script (if needed)

Only needed when a tool requires the secret as an environment variable at startup (e.g., MCP servers). Skip this step for secrets read on demand (e.g., curl API calls with $(security ...)).

Template — save as scripts/run-<tool>.sh:

#!/usr/bin/env bash
set -euo pipefail

export <ENV_VAR_NAME>
<ENV_VAR_NAME>="$(security find-generic-password -s '<service>' -a '<account>' -w)"

exec <original-command> "$@"

Key details:

  • set -euo pipefail — fail fast on any error
  • Declare export and assignment on separate lines — if they're combined (export VAR=$(cmd)), a failing command won't trigger set -e
  • exec replaces the shell process with the actual command — no leftover wrapper process
  • "$@" passes through any additional arguments

Make the script executable:

chmod +x scripts/run-<tool>.sh

Step 4: Update configuration

Update the relevant configuration files to use the wrapper script instead of the original command.

For MCP servers (.mcp.json):

Before:

{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "--directory", ".", "my-server"]
    }
  }
}

After:

{
  "mcpServers": {
    "my-server": {
      "command": "bash",
      "args": ["scripts/run-my-server.sh"]
    }
  }
}

For on-demand API calls (no wrapper needed):

curl -H "PRIVATE-TOKEN: $(security find-generic-password -s 'gitlab-pat' -a 'claude-code' -w)" \
  https://gitlab.example.com/api/v4/...

Step 5: Remove plaintext secrets

Remove the secret from any plaintext file where it previously lived:

  • .env files — comment out the line and add a pointer to the Keychain entry:
    # Stored in macOS Keychain (service: <service>, account: <account>)
    # <ENV_VAR_NAME>=
    
  • Config files — same pattern: remove the value, leave a comment

Never delete the entire variable reference — the comment serves as documentation for other developers or future-you about where the secret lives.

Step 6: Secure .gitignore

Check that these files are in .gitignore:

  • .env (should already be there — verify)
  • .mcp.json (contains local paths and tool config)
  • Any other file that held the secret

Add missing entries. Don't duplicate existing ones.

Step 7: Verify end-to-end

Run the wrapper script or the tool that consumes the secret and confirm it works. For MCP servers, a quick smoke test:

# Inline Python test — adapt imports to the project
from dotenv import load_dotenv
load_dotenv()
# ... initialize client, make a simple API call, confirm success

Or just launch the tool and check it connects successfully.

Report the final state to the user:

ComponentStatus
Keychain entry<service> / <account>
Wrapper scriptscripts/run-<tool>.sh (or N/A if on-demand)
Config updated<which file>
Plaintext removed<which file>
.gitignoreUpdated
VerificationPass/Fail

Managing existing secrets

List Keychain entries for a project

security find-generic-password -a '<account>' 2>&1 | grep "svce"

List all Claude Code secrets

security find-generic-password -a 'claude-code' 2>&1 | grep "svce"

Update a secret

Delete and re-add (the -U flag is unreliable on some macOS versions):

security delete-generic-password -s '<service>' -a '<account>'
security add-generic-password -s '<service>' -a '<account>' -w

Delete a secret

security delete-generic-password -s '<service>' -a '<account>'

Multiple secrets in one project

When a project needs several secrets (e.g., an API token and a database password), create one Keychain entry per secret and extend the wrapper script:

#!/usr/bin/env bash
set -euo pipefail

export API_TOKEN DB_PASSWORD

API_TOKEN="$(security find-generic-password -s 'myapp-api-token' -a 'my-project' -w)"
DB_PASSWORD="$(security find-generic-password -s 'myapp-db-password' -a 'my-project' -w)"

exec my-command "$@"

Constraints

  • macOS only — this skill uses security(1) which is a macOS-specific tool. For Linux, suggest secret-tool (libsecret) or pass. For cross-platform, suggest 1password-cli or environment-specific vaults.
  • Local development only — CI/CD and production environments should use their platform's native secret management (GitHub Secrets, GitLab CI variables, AWS Secrets Manager, etc.).
  • One secret per Keychain entry — don't try to store JSON blobs or multiple values in a single entry.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.