Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx skills add mayank-io/mstack --skill clean-permissionsgit clone --depth 1 https://github.com/mayank-io/mstackWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/mayank-io/mstack/clean-permissions)<a href="https://agentmods.dev/skills/mayank-io/mstack/clean-permissions"><img src="https://agentmods.dev/badge/skills/mayank-io/mstack/clean-permissions/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/skills/mayank-io/mstack/clean-permissions"><img src="https://agentmods.dev/badge/skills/mayank-io/mstack/clean-permissions.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00055 | $0.01969 |
| Opus 5.5 | $0.00022 | $0.00788 |
| Sonnet 5.5 | $0.00011 | $0.00394 |
| Haiku 4.5 | $0.00006 | $0.00197 |
Grade A, and why
clean-permissions scanned grade A with 2 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2mo ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Reads agent configuration directorieslowAgent snooping
.claude/, .codex/, .gemini/ hold keys, settings and other credentials a mod has no legitimate need for.
Remove cruft from `~/.claude/settings.json` permissions allow list. Three categories of junk accumulate over time: Downgraded: this mod is about security review, or the phrase is quoted, so it is likely naming the pattern rather than instructing it.
Makes network callslowCapability
Not a fault in itself. Listed so you know the mod talks to something, and to what.
r'^Bash\(/usr/bin/curl ', How it starts
The opening of the file, as written. The whole thing — 208 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Clean Permissions Allow List
Remove cruft from ~/.claude/settings.json permissions allow list. Three categories of junk accumulate over time:
- One-off command pastes — full multi-line scripts, hardcoded IDs/paths/dates that got auto-approved during sessions
- Redundant entries — specific entries subsumed by a broader wildcard already in the list (e.g.
WebFetch(domain:example.com)whenWebFetch(domain:*)exists) - Dead references — MCP tools for servers that have been removed or disabled
Step 1: Read current state
Read ~/.claude/settings.json and extract the permissions.allow array. Report the current count.
Step 2: Run the cleanup script
Run this Python script via Bash to identify and remove cruft. The script is non-destructive until the user confirms — it prints a plan first.
IMPORTANT: Always run this script exactly. Do not improvise the cleanup logic.
import json, os, re, sys
with open(os.path.expanduser('~/.claude/settings.json')) as f:
settings = json.load(f)
allow = settings['permissions']['allow']
before = len(allow)
print(f"Current entries: {before}")
# === Build wildcard index for redundancy detection ===
#
# Permission format semantics:
# Bash(COMMAND_PREFIX:*) — matches any Bash command starting with COMMAND_PREFIX
# mcp__SERVER__* — matches any MCP tool on that server
# WebFetch(domain:*) — matches any domain
#
# So Bash(gh pr:*) covers Bash(gh pr create:*) because "gh pr create"
# starts with "gh pr". We extract the command prefix for semantic matching.
bash_wildcards = [] # list of command prefixes from Bash(CMD:*) entries
mcp_wildcards = [] # list of prefixes from mcp__X__* entries
has_webfetch_star = False
for e in allow:
# Bash(gh pr:*) -> command prefix "gh pr"
m = re.match(r'^Bash\((.+?):\*\)$', e)
if m:
bash_wildcards.append(m.group(1))
continue
# mcp__playwright__* -> prefix "mcp__playwright__"
if e.endswith('__*') and e.startswith('mcp__'):
mcp_wildcards.append(e[:-1]) # "mcp__playwright__"
continue
# WebFetch(domain:*)
if e == 'WebFetch(domain:*)':
has_webfetch_star = True
continue
# === Detect one-off pastes (category 3) ===
oneoff_patterns = [
r'^Bash\(for id in ',
r'^Bash\(do echo ',
r'^Bash\(do\)$',
r'^Bash\(done\)$',
r'^Bash\(fi\)$',
r'^Bash\(# ',
r'^Bash\(echo === ',
r'^Bash\(echo .+?:\*\)$',
r'^Bash\(export (PATH|GOPATH)=',
r'^Bash\(/usr/bin/curl ',
r'^Bash\(/opt/homebrew/bin/jq ',
]
cat3_oneoff = set()
for e in allow:
# Multi-line bash -c scripts (NOT the wildcard Bash(bash -c:*))
if e.startswith("Bash(bash -c '") and e != "Bash(bash -c:*)":
cat3_oneoff.add(e)
continue
for pat in oneoff_patterns:
if re.match(pat, e):
cat3_oneoff.add(e)
break
# === Detect redundant entries (category 2) ===
cat2_redundant = set()
for e in allow:
if e in cat3_oneoff:
continue
# Check Bash entries: Bash(CMD:*) is redundant if a shorter CMD prefix exists
m = re.match(r'^Bash\((.+?):\*\)$', e)
if m:
cmd = m.group(1)
for wc_cmd in bash_wildcards:
if cmd != wc_cmd and cmd.startswith(wc_cmd):
cat2_redundant.add(e)
break
continue
# Check non-wildcard Bash entries: Bash(FULL_CMD) covered by Bash(PREFIX:*)
m2 = re.match(r'^Bash\((.+?)\)$', e)
if m2 and not e.endswith(':*)'):
cmd = m2.group(1)
for wc_cmd in bash_wildcards:
if cmd.startswith(wc_cmd):
cat2_redundant.add(e)
break
continue
# Check MCP entries: individual tool covered by server wildcard
if e.startswith('mcp__') and not e.endswith('__*'):
for wc_prefix in mcp_wildcards:
if e.startswith(wc_prefix):
cat2_redundant.add(e)
break
continue
# Check WebFetch: specific domain redundant with WebFetch(domain:*)
if has_webfetch_star and e.startswith('WebFetch(domain:') and e != 'WebFetch(domain:*)':
cat2_redundant.add(e)
continue
# === Detect dead MCP refs (category 1) ===
# Known plugin-provided MCP prefixes that don't need a mcpServers entry
plugin_mcp = {'context7', 'playwright', 'plugin_playwright_playwright',
'plugin_context7_context7', 'plugin_serena_serena'}
mcp_servers = settings.get('mcpServers', {})
active_servers = {k for k, v in mcp_servers.items() if not v.get('disabled', False)}
cat1_dead = set()
for e in allow:
if e in cat2_redundant or e in cat3_oneoff:
continue
m = re.match(r'^mcp__(.+?)__', e)
if m:
server = m.group(1)
if server not in active_servers and server not in plugin_mcp:
cat1_dead.add(e)
# === Consolidate individual mcp__plugin_* to wildcards ===
plugin_groups = {}
for e in cat2_redundant:
m = re.match(r'^(mcp__plugin_\w+__)', e)
if m:
plugin_groups.setdefault(m.group(1), []).append(e)
new_wildcards = []
for prefix, entries in plugin_groups.items():
wc = prefix + '*'
if wc not in allow:
new_wildcards.append(wc)
# === Report ===
remove_set = cat1_dead | cat2_redundant | cat3_oneoff
print(f"\n--- Category 1: Dead MCP ({len(cat1_dead)}) ---")
for e in sorted(cat1_dead):
print(f" {e}")
print(f"\n--- Category 2: Redundant ({len(cat2_redundant)}) ---")
for e in sorted(cat2_redundant):
print(f" {e}")
print(f"\n--- Category 3: One-off pastes ({len(cat3_oneoff)}) ---")
for e in sorted(cat3_oneoff):
print(f" {e[:80]}{'...' if len(e) > 80 else ''}")
if new_wildcards:
print(f"\n--- New wildcards to add ({len(new_wildcards)}) ---")
for w in new_wildcards:
print(f" {w}")
after = before - len(remove_set) + len(new_wildcards)
print(f"\nBefore: {before} | Removing: {len(remove_set)} | Adding: {len(new_wildcards)} | After: {after}")
if '--apply' not in sys.argv:
print("\nDry run. Pass --apply to write changes.")
sys.exit(0)
# === Apply ===
cleaned = [e for e in allow if e not in remove_set]
cleaned.extend(new_wildcards)
settings['permissions']['allow'] = cleaned
with open(os.path.expanduser('~/.claude/settings.json'), 'w') as f:
json.dump(settings, f, indent=2)
f.write('\n')
print(f"Written. Final count: {len(cleaned)}")
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 2mo ago First seen · 208 lines · 55 tokens per session scan A 0ccf6269f481
clean-permissions is a skill published in the GitHub repository mayank-io/mstack (6 stars, last pushed 3d ago), licensed MIT. It adds 55 tokens to every session and 1,969 once invoked, about $0.0002 per session on Opus 5.5. A static security scan graded it A with 2 findings (reads agent configuration directories, makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.
Other skills, from other repositories
local-ai-agents
Build local-first AI agents that run entirely on a developer workstation with Microsoft Foundry Local and Qwen function-calling models. Covers Small Language Models (SLMs), the OpenAI-compatible local endpoint, sandboxed local tools, local RAG with Chroma, local MCP servers, hybrid cloud/local routing, and the…
systematic-debugging
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes.
next-cache-components-adoption
Turn on Cache Components in a Next.js app and resolve the blocking routes it surfaces. Use when the user wants to enable, adopt, or migrate to Cache Components, flip the cacheComponents flag, work through a flood of blocking-prerender / instant validation errors, run the cache-components-instant-false codemod, or…
chronicle
Analyze Copilot session history for standup reports, usage tips, session search, and session reindexing. Use when the user asks for a standup, daily summary, usage tips, workflow recommendations, wants to search or find past sessions by keyword/file/PR, wants to reindex their session store, or asks about deleting…
chat-pet-sprite-creation
Use when creating or changing VS Code chat pet sprite art, sprite sheets, state animations, eye treatments, Stable/Insiders variants, or pet transitions under src/vs/workbench/contrib/chat/browser/widget/media/chatPet.
component-fixtures
Use when creating or updating component fixtures for screenshot testing or their shared infrastructure, or when designing UI components to be fixture-friendly. Covers fixture file structure, theming, service setup, CSS scoping, async rendering, validation, and common pitfalls.