Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/validate-skills.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# GitHub Action: Validate Agent Skills
#
# Validates every skill under skills/ (any depth) against the agentskills.io spec using
# skills-ref (https://github.com/agentskills/agentskills). Fails if any skill's
# SKILL.md frontmatter or referenced files are invalid.
#
# Maintainer: Matt Norris <matnorri@cisco.com>
#
# For more information on Agent Skills, see: https://agentskills.io/specification

name: Validate Skills
on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
paths:
- 'skills/**'
- '.github/workflows/validate-skills.yml'

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Validate each skill
run: |
set -euo pipefail
status=0
while IFS= read -r skill_md; do
skill="$(dirname "$skill_md")"
echo "== validating ${skill} =="
uvx --from git+https://github.com/agentskills/agentskills#subdirectory=skills-ref \
skills-ref validate "$skill" || status=1
done < <(find skills -type f -name SKILL.md | sort)
exit $status
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,9 @@ output/

# Claude
.claude/settings.json

# Agent-skills install output from `npx skills add ./skills` (source of truth is
# skills/ess/). The CLI installs to .agents/skills/ and writes a machine-specific
# lock file.
.agents/skills/
skills-lock.json
53 changes: 53 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Skills

[Agent Skills](https://agentskills.io) — portable `SKILL.md` workflows that work across Cursor, Claude Code, Codex, and other compatible agents.

The skills live under [`ess/`](ess/). Each is self-contained (`SKILL.md` plus optional `scripts/`, `references/`, and a `LICENSE`) and host-neutral: no IDE-specific frontmatter committed.

## Install

Install with the standard agent-skills CLI ([skills.sh](https://skills.sh)). It auto-detects your agent and places files in the right directory.

From a local clone (run in the repo root):

```bash
npx skills add ./skills --skill '*' --full-depth # all skills
npx skills add ./skills --skill mcp-hide-secrets --full-depth # a single skill
npx skills add ./skills --list --full-depth # list without installing
```

`--full-depth` is required so the CLI discovers skills under nested paths like `skills/ess/` (not just top-level directories).

The CLI writes project skills to `.agents/skills/<name>/` (which Cursor reads natively, alongside `.cursor/skills/`). These are gitignored install output — the source of truth stays in `skills/`.

Or straight from GitHub:

```bash
npx skills add CiscoDevNet/essentials --skill '*' --full-depth
```

### Install matrix

| Agent | Project path | Global (`-g`) |
| ----- | ------------ | ------------- |
| Cursor | `.agents/skills/<name>/` (also reads `.cursor/skills/<name>/`) | `~/.cursor/skills/<name>/` |
| Claude Code | `.claude/skills/<name>/` | `~/.claude/skills/<name>/` |
| Codex | `.agents/skills/<name>/` | `~/.agents/skills/<name>/` |

## Skills

| Skill | What it does |
| ----- | ------------ |
| [`ess/pr-address-comments`](ess/pr-address-comments/) | Read a remote GitHub PR's review comments, implement fixes, then reply to and resolve the addressed threads. |
| [`ess/pr-address-comments-all`](ess/pr-address-comments-all/) | Address review comments across one or more PRs in parallel, each in its own git worktree on the PR's branch. |
| [`ess/mcp-hide-secrets`](ess/mcp-hide-secrets/) | Move Cursor MCP credentials out of `mcp.json` into a login-loaded `mcp.env` (macOS LaunchAgent). |
| [`ess/summarize-change-log`](ess/summarize-change-log/) | Condense a long git log or squash message into 1–5 Conventional-Commit bullets. |

## Notes

- Cursor-only behavior such as `disable-model-invocation: true` (slash-command-only activation) is kept out of the committed `SKILL.md`. To enable it locally, see [`ess/mcp-hide-secrets/references/install-cursor.md`](ess/mcp-hide-secrets/references/install-cursor.md).
- `pr-address-comments-all` invokes its scripts from their committed location (`skills/ess/pr-address-comments-all/scripts/`), so they are present in every checkout and worktree of a repo that vendors these skills.

## License

[Apache 2.0](../LICENSE) — each skill also carries its own `LICENSE`.
13 changes: 13 additions & 0 deletions skills/ess/mcp-hide-secrets/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2025 Cisco Systems, Inc. or its Affiliates

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.
103 changes: 103 additions & 0 deletions skills/ess/mcp-hide-secrets/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
name: mcp-hide-secrets
description: >-
Secure Cursor MCP credentials on macOS by moving secrets from ~/.cursor/mcp.json
or a project .cursor/mcp.json into ~/.local/share/cursor-mcp/mcp.env and loading
them via a LaunchAgent at login. Use when the user runs /mcp-hide-secrets, asks to
secure MCP config, externalize MCP secrets, set up mcp.env, or fix MCP auth after
cleaning up mcp.json. macOS only — do not use on Windows or Linux.
compatibility: >-
macOS only. Requires bash, python3, launchctl, and write access to
~/Library/LaunchAgents. Windows support is planned via scripts/windows/ (not yet
implemented).
metadata:
version: "1.0"
supported_os: macos
---

# MCP Hide Secrets (macOS — LaunchAgent + .env)

Move live credentials out of `~/.cursor/mcp.json` and/or a project `.cursor/mcp.json` into `~/.local/share/cursor-mcp/mcp.env`, loaded at login by a macOS LaunchAgent via `launchctl setenv`. Cursor interpolates `${env:VAR}` in MCP `env` and `headers` blocks.

## Agent workflow (required)

**Run only `scripts/run.sh`.** Do not write ad-hoc preflight, migrate, or verify commands.

```bash
# From this skill root (set PROJECT_MCP_JSON when a repo .cursor/mcp.json should be included):
chmod +x scripts/run.sh
USERNAME="$(whoami)" PROJECT_MCP_JSON="<repo>/.cursor/mcp.json" scripts/run.sh
USERNAME="$(whoami)" scripts/run.sh migrate
USERNAME="$(whoami)" scripts/run.sh status
scripts/run.sh fix-launchagents # sudo in Terminal when agent cannot prompt
```

Never read or display secret values from `mcp.json` or `mcp.env`. Report script output only.

| User says | Script |
| --------- | ------ |
| `/mcp-hide-secrets` | `scripts/run.sh` (default) |
| `/mcp-hide-secrets migrate` | `scripts/run.sh migrate` [optional `mcp.json` path] |
| `/mcp-hide-secrets status` | `scripts/run.sh status` |
| `/mcp-hide-secrets install` | `scripts/run.sh install` (after `fix-launchagents`) |

If sudo fails in a non-interactive shell, ask the user to run `scripts/fix-launchagents-dir.sh` in Terminal, then `USERNAME="$(whoami)" scripts/run.sh install`.

After success: user quits Cursor (Cmd+Q) and reopens from Dock.

## MCP config targets

| Location | Path |
| -------- | ---- |
| Global | `~/.cursor/mcp.json` |
| Project | `<repo>/.cursor/mcp.json` |

Both merge into `~/.local/share/cursor-mcp/mcp.env`. Default `run.sh` migrates global first, then project when `PROJECT_MCP_JSON` is set. Migrate dirty files separately with `scripts/run.sh migrate <path>`.

## Gotchas (keep in SKILL.md)

- Dock-launched Cursor does **not** see `~/.zshrc` exports — session env via LaunchAgent is required.
- `~/Library/LaunchAgents` is sometimes owned by `root`; `fix-launchagents-dir.sh` is a normal step, not a one-off fix.
- Migrate runs **before** LaunchAgent install so secrets move even when sudo is pending.
- A server named `github` (any case) drops `headers` on migrate so OAuth can run.

## Migration rules (summary)

| Source in `mcpServers` | In `mcp.env` | In `mcp.json` |
| ---------------------- | ------------ | ------------- |
| `env` literals | same key | `${env:KEY}` |
| `headers` literals | `MCP_<SERVER>_<HEADER>` | `${env:MCP_…}` |

Identifiers (`X-User-ID`, `ATLASSIAN_USER_EMAIL`, …) stay inline. Details: [references/launchctl.md](references/launchctl.md).

## References (read when needed)

| File | When |
| ---- | ---- |
| [references/install-cursor.md](references/install-cursor.md) | Cursor-only optional steps (slash command, `disable-model-invocation`) |
| [references/launchctl.md](references/launchctl.md) | Manual install, migration tables, ongoing ops |
| [references/rationale.md](references/rationale.md) | Why session env instead of per-server `envFile` |
| [references/launchagent-naming.md](references/launchagent-naming.md) | Label `local.<username>.cursor-mcp-env`, launchctl commands |

## Scripts

| Script | Role |
| ------ | ---- |
| `run.sh` | Orchestrator — **only agent entry point** |
| `preflight.sh` | Read-only checks |
| `status.sh` | Read-only report |
| `check-inline-secrets.py` | Scan `mcp.json` without printing values |
| `install.sh` | Migrate-only or LaunchAgent install (called by `run.sh`) |
| `fix-launchagents-dir.sh` | `sudo chown "$(whoami):staff" ~/Library/LaunchAgents` |
| `verify.sh` | Post-install session env check |

## Files on the user's Mac

| Path | Purpose |
| ---- | ------- |
| `~/.local/share/cursor-mcp/mcp.env` | Secrets (chmod 600, never commit) |
| `~/.local/share/cursor-mcp/load-mcp-env.sh` | Parser + `launchctl setenv` |
| `~/Library/LaunchAgents/local.<username>.cursor-mcp-env.plist` | Login hook |
| `~/.cursor/mcp.json` and/or project `.cursor/mcp.json` | Sanitized MCP config |

Rotate credentials that were ever committed or pasted into chat before or after migration.
44 changes: 44 additions & 0 deletions skills/ess/mcp-hide-secrets/references/install-cursor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Cursor installation (optional)

This skill is portable per [agentskills.io](https://agentskills.io/specification). The committed `SKILL.md` stays host-neutral; apply these steps when installing under **Cursor** locally or in a team monorepo fork.

## Slash command only (`/mcp-hide-secrets`)

Cursor can load a skill for explicit slash invocation without auto-attaching it to every agent turn. Add this line to the **top-level** YAML frontmatter in your local copy of `SKILL.md` (not under `metadata` — Cursor reads the top-level key):

```yaml
disable-model-invocation: true
```

Example (after `description`, before `compatibility`):

```yaml
---
name: mcp-hide-secrets
description: >-
...
disable-model-invocation: true
compatibility: >-
macOS only. ...
---
```

**Why not in the published skill?** That key is Cursor-specific and not part of the agentskills.io spec. Keeping it out of the committed skill lets `skills-ref validate` pass and lets Claude Code / other hosts use the same files without host-only frontmatter.

**Team monorepo forks:** A Cursor-first monorepo may commit `disable-model-invocation: true` directly in `SKILL.md`. For a portable publish copy, leave it out and document this file instead.

## Run the skill

After optional frontmatter edits:

```
/mcp-hide-secrets
```

Or from the skill root:

```bash
USERNAME=$(whoami) PROJECT_MCP_JSON="<repo>/.cursor/mcp.json" scripts/run.sh
```

See [launchctl.md](launchctl.md) for manual install, migration, and ongoing ops.
37 changes: 37 additions & 0 deletions skills/ess/mcp-hide-secrets/references/launchagent-naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# LaunchAgent label for this skill

This skill installs one LaunchAgent. Keep the **filename** and plist **`Label`** identical.

| Item | Value |
| ---- | ----- |
| Label | `local.<your-username>.cursor-mcp-env` |
| Plist path | `~/Library/LaunchAgents/local.<your-username>.cursor-mcp-env.plist` |
| Bootstrap domain | `gui/$(id -u)` |

Replace `<your-username>` with your macOS username (`whoami`). Use kebab-case if the username contains underscores.

## Why `local.<username>.…`

- **`local.`** — personal namespace, not a vendor domain (avoids squatting `com.*` for one-off scripts).
- **`<username>`** — collision-safe within shared machines; matches `USERNAME` passed to `scripts/run.sh`.
- **`cursor-mcp-env`** — service purpose, kebab-case.

`launchd` only requires the label be unique in your user domain; the shape above is convention for scanability in `launchctl list` and logs.

## Managing the agent

```bash
# Load (install.sh does this)
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.$(whoami).cursor-mcp-env.plist

# Re-run loader after editing mcp.env
launchctl kickstart -k gui/$(id -u)/local.$(whoami).cursor-mcp-env

# Inspect
launchctl print gui/$(id -u)/local.$(whoami).cursor-mcp-env

# Remove
launchctl bootout gui/$(id -u)/local.$(whoami).cursor-mcp-env
```

Commands use the **Label**, not the `.plist` filename — keeping them in sync avoids copy-paste mistakes.
Loading
Loading