Skip to content

Dr. Skill: Auditing Your Agent's Skill and MCP Loadout

Dr. Skill (drskill) is a read-only diagnostic CLI that resolves which skills and MCP servers each coding agent on a machine actually loads, then checks that whole set against 34 issue categories: duplicates, shadowed names, colliding descriptions, prompt-injection surfaces, and unpinned MCP packages. It never edits a file, calls a model, or connects to a server unless explicitly asked.

Every skill you install and every MCP server you connect adds text to the context before you type a word. That cost is invisible: no tool tells you the running total, nothing warns you when two skills describe themselves so similarly that the model cannot pick between them, and nothing notices when a server you approved months ago quietly rewrites what its tools claim to do. You find out the way everyone finds out — the agent reaches for the wrong thing and you cannot explain why.

  • drskill is a Python CLI, not a skill. It does not install through npx skills add, and your agent does not load it.
  • It answers a question no other tool in this section answers: what is my agent loading right now, across every harness on the machine, and is any of it fighting itself?
  • It is read-only by design. No LLM call without --deep, no MCP server contacted without --mcp-connect, and it never edits or deletes a skill under any flag.
  • 34 checks span skill hygiene (shadowing, duplicates, spec violations, token budgets), description quality (overlap, missing triggers, opposing instructions), prompt-injection surfaces, and MCP configuration.
  • drskill audit is the part people underestimate: it reads the session traces your agents already write to disk and ranks what actually got invoked, so you can delete on evidence instead of vibes.
  • Its findings end in a command — either a fix or an ack that records your decision in a committed ledger, fingerprinted so the finding returns if the underlying skill changes.

Why a big skill catalog quietly degrades your agent

Section titled “Why a big skill catalog quietly degrades your agent”

The failure is not that skills are bad. It is that a skill catalog is a tool loadout, and loadouts have a size past which selection accuracy falls apart.

Breunig named this pattern in How to Fix Your Context — “the act of selecting only relevant tool definitions to add to your context” — and the numbers behind it are blunt. The RAG-MCP paper found DeepSeek-v3’s performance degrading badly past 30 tools, and beyond 100 tools the model was virtually guaranteed to fail their test. The “Less is More” paper found Llama 3.1 8b failing a benchmark given 46 tools and succeeding on the same benchmark given 19; selecting tools dynamically improved its score on the Berkeley Function Calling Leaderboard by 44%, while cutting power draw 18% and improving speed 77%.

When Breunig wrote that piece, he noted it was hard to find real-world examples of the pattern. A year later the examples find you. He met a developer whose enterprise agent was loading over 600 skills — everything every colleague had ever written, pulled in by default, with no notice to the developer and no way to see it. His own Hermes agent kept reaching for the wrong note-taking skill; SSH-ing into the box, he found Hermes ships with nearly 100 skills. Deleting the stock note-taker helped and did not fix it.

The mechanics inside Claude Code make this concrete. A skill’s description is resident in context from the moment the session starts so the model knows the skill exists; only the body loads on invocation. That is the right design, and it means your catalog’s cost scales with the number of skills installed, not the number used. It also means two skills with similar descriptions are not a cosmetic problem — they are a routing problem, because the description is the entire basis on which the model chooses.

The tail is worse. After auto-compaction, Claude Code re-attaches the most recent invocation of each skill, keeping the first 5,000 tokens of each within a combined 25,000-token budget, filled newest-first. Invoke enough skills in one session and the older ones fall out entirely — silently, mid-task.

So there are two distinct costs, and they need separate treatment: a token bill you pay every session for skills you may never call, and a routing tax you pay whenever two entries in the catalog are hard to tell apart. drskill measures the first and detects the second.

Where Dr. Skill sits next to the skills CLI and SkillSpector

Section titled “Where Dr. Skill sits next to the skills CLI and SkillSpector”

This section already covers two tools that sound adjacent and are not. The distinction is when in the lifecycle each one runs.

Question it answersScopeRuns
npx skillsHow do I install, update, and pin this?One skill or repo at a timeOn demand, mutates your skill directories
SkillSpectorShould I install this skill at all?One skill, repo, or archive, before it landsPre-install gate
drskillWhat did all of that add up to, and does it conflict?Every harness on the machine, after install, plus MCP serversPost-install diagnosis

The gap drskill fills is the resolved set. npx skills list tells you what a lockfile says you installed. drskill works out what each harness effectively loads after precedence rules, nested directories, symlinks, machine-level skills, and shadowing — which is frequently not the same list. And it is the only one of the three that looks at MCP servers at all, even though tool definitions and skill descriptions land in exactly the same context and compete for exactly the same routing decision.

What you’ll walk away with from a loadout audit

Section titled “What you’ll walk away with from a loadout audit”
  • A reading of the tool-loadout problem specific enough to act on: which of your two costs, token bill or routing tax, you are actually paying
  • The three commands that matter (scan, list, audit) and what each one will and will not touch
  • A CI recipe that fails a pull request on an unacknowledged warning without turning every judgment call into a permanent exception
  • Prompts that turn raw findings into a triage list, a rewritten description, and a delete list backed by usage data
  • The limits — including one that means a clean scan is not a clean loadout

drskill is published to PyPI and installs as a standalone command. The full install includes the model-judged deep checks and MCP connection support:

Terminal window
uv tool install drskill

For CI, where neither of those is used, the core package is smaller and has fewer dependencies:

Terminal window
uv tool install drskill-core

pip install drskill works too if uv is not in your toolchain.

Then run it from a project root:

Terminal window
drskill scan

That detects every coding agent it can find, resolves each one’s effective skill set, and prints a report grouped by severity. Each finding names the harnesses it affects and ends in either a fix command or an ack command.

The 34 checks fall into three groups, and the grouping matters because they demand different responses.

Skill hygiene is the mechanical layer. name-shadow fires when two skills share a name inside one harness’s set and one silently wins — the message names the winner and the precedence rule that picked it. double-load is an error, not a warning: one harness loading the same logical skill through two directories, paying for it twice. exact-duplicate and near-duplicate catch copies under different names, the latter on MinHash Jaccard similarity at or above a configurable 0.85. spec-name-mismatch, spec-missing-description, spec-invalid-frontmatter, and spec-description-too-long (over 1024 characters, the ceiling the Agent Skills spec sets) catch files the harness will load wrong or skip. budget-catalog-tokens and budget-body-tokens are your token bill, measured against thresholds you set.

Description quality is the routing tax, and this is the group most worth your attention. description-overlap flags clusters of descriptions similar enough that a router could confuse them, naming the shared trigger phrases. missing-activation fires when a description never says when to use the skill — no “when”, no “if the user”, no trigger condition at all — which is the single most common reason a well-written skill never fires. generic-description catches “Helps with various tasks.” opposing-imperatives catches two skills giving opposite orders about the same action, the “always use tabs” versus “never use tabs” case.

Injection surfaces are the security layer, and they overlap with — but do not replace — a pre-install scanner. Seven checks look for bidirectional or zero-width Unicode that hides text from a human reviewer, references to ~/.ssh or ~/.aws in bundled scripts, “ignore all previous instructions” phrasing, skills that demand their own setup script run first, network egress from bundled scripts, long base64 blobs, and instructions telling the agent to fetch remote content and act on it.

The MCP checks are the half of the loadout no other tool in this section touches: the same server configured twice with drifted settings, a credential-shaped literal sitting in a committable config file (an error in project files, a warning in user files), npx -y and @latest packages that run whatever publishes next, plaintext http:// remotes, and stdio commands whose binary is no longer on PATH.

Exit codes are designed so local runs stay quiet and CI does not:

Command012
drskill scanClean, or every finding acknowledgedAn error-level finding is activeOnly warnings active, and --ci was passed
drskill lintNothing at or above the thresholdA finding at or above the thresholdUsage error — you pointed it at the wrong thing

Without --ci, warnings alone exit 0, so drskill scan in your shell never fails your shell.

scan reports problems. list reports the inventory, which is what you want when the question is “how did it get this big.”

Terminal window
drskill list --tokens

This prints each harness’s effective skill set with token counts. Two columns carry more information than their width suggests. source records provenance: skills-lock for skills a project’s skills-lock.json names, gh-skill for skills carrying gh skill provenance in frontmatter, linked for skills living in or linking into a .agents/skills store, and unmanaged for a plain directory with no known manager. A loadout that is mostly unmanaged is a loadout nobody can reproduce on a teammate’s machine.

suite names where a row came from — the plugin, the repo, or, for an MCP tool, the server exposing it. Rows sort by suite, so a suite reads as one block and you can see the size of a bundle at a glance rather than as fourteen scattered lines.

To see the MCP half on its own:

Terminal window
drskill list --mcp

A configured server shows as a single row until you run --mcp-connect; after that it expands into one row per tool it exposes.

This is the command that changes behavior. scan looks at the loadout as configured; drskill audit looks at how you actually used it, by reading the session traces Claude Code, Codex, Pi, and Copilot already write to disk.

Terminal window
drskill audit

The project’s documentation shows output in this shape:

claude-code coverage: 2026-07-20 to 2026-07-24 · 6 sessions · 57 invocations
name kind uses share sessions last used
superpowers:brainstorming skill 14 25% 6 2026-07-23
superpowers:writing-plans skill 8 14% 5 2026-07-23
plain-writing skill 6 11% 5 2026-07-23

Widen the window to every project on the machine:

Terminal window
drskill audit --global --since 30d

Then drill into a single skill to see the queries that led to it — the full user message that preceded each invocation, how it was triggered (an explicit tool call, a slash command, or a SKILL.md read), and the exact trace file and line so you can open the transcript at that moment:

Terminal window
drskill audit overturemaps

Because a skill name and an MCP tool name can collide, the drill-down also takes a server:tool form:

Terminal window
drskill audit browser:get_screenshot --global

Audit only reads. It writes nothing to the ledger, creates no findings, and has no effect on --ci.

Four caveats keep the numbers honest. On Codex and Pi, a skill count comes from observing the agent read that skill’s SKILL.md, not from an explicit invocation event — those rows carry a ~ marker. Codex encrypts its reasoning, so audit cannot show reasoning for Codex invocations. Copilot records neither reasoning nor the structured arguments of a tool call, so its drill-downs are thinner. And each harness retains traces for a different length of time, so a raw cross-harness count comparison misleads; the rollup at the bottom of the report ranks by invocations per week within each harness’s own coverage window, and says so when the windows differ a lot.

That last group is the one people miss. The instinct on seeing zero invocations is to delete, but a skill that never fires is often a skill whose description never told the router when to fire it. missing-activation and the audit disagree on the same skill for a reason worth reading.

Running it for Cursor, Claude Code, and Codex

Section titled “Running it for Cursor, Claude Code, and Codex”

drskill scans every harness it detects in one pass, so the base command is identical everywhere. What differs is which config files it reads, how confident it is about the precedence rules, and whether traces exist for audit.

Scope a scan to Cursor:

Terminal window
drskill scan --harness cursor

For skills it reads .cursor/skills and .agents/skills in the project, plus ~/.cursor/skills and ~/.agents/skills on the machine, walking each recursively. For the MCP half it reads .cursor/mcp.json and ~/.cursor/mcp.json.

Cursor is verified on paths only. The project confirmed which directories Cursor reads and whether it searches them recursively, but Cursor’s docs do not say which copy wins when two skills share a name, so precedence is unverified. That distinction is surfaced rather than hidden: shadowing and double-load findings depend on precedence and will carry a ? suffix on the harness name, while every other finding depends only on paths and carries no marker. A finding with no ? rests entirely on verified rules.

Cursor is not among the harnesses whose session traces drskill audit reads, so the usage half of this workflow comes from your Claude Code and Codex sessions.

An unknown harness id is an error that names the valid ids, and harnesses that are detected but load no skills are hidden from the tables by default — a closing line names them, and --all shows them.

The reason to wire this into CI is not that loadouts are usually broken. It is that they drift: a git pull brings in a teammate’s new skill, an unpinned MCP package publishes a new version, a server rewrites a tool description after you approved it.

Terminal window
drskill scan --ci

With --ci, an unacknowledged warning fails the build. The escape hatch is the ledgerdrskill.toml at the repo root, committed:

[budget]
catalog_tokens_max = 50000
body_tokens_warn = 5000
[thresholds]
near_duplicate = 0.85
description_overlap = 0.6
generic_min_distinct_tokens = 2

drskill ack appends decisions to the end of that file and touches nothing else, so your comments and formatting survive:

[[ack]]
check = "near-duplicate"
skills = ["docx-report", "documentation-writer"]
fingerprint = "sha256:..."
note = "docx is output format specific; keeping both"
date = 2026-07-19

The design detail worth copying into your own tooling: a finding’s fingerprint is a hash of the check id plus the content of every skill involved. An ack silences the finding only while that fingerprint matches. Edit one of those skills and the ack stops applying, so the finding returns on the next scan. An ack means “this exact situation is fine,” not “never check this pair again” — which is what makes a green build after an ack still worth something.

Acks are scope-aware. When a finding involves only machine-level skills, the ack is written to ~/.drskill.toml; when a project skill is involved, it goes in the committed project ledger. --local and --global-ack override the routing.

If you publish skills or plugins rather than only consuming them, drskill lint turns the same checks on what you author:

Terminal window
drskill lint ./my-plugin --fail-on warn --json

It works out what you pointed it at — a plugin directory with a plugin.json, a skill folder or its SKILL.md, or an MCP config file — and checks a plugin against the Agent Plugins specification on top of running the content checks over everything inside.

One check deserves its own explanation, because the underlying feature is easy to miss.

A skill file is usually just text the agent reads. Claude Code lets it be more: a skill can carry shell commands that run the moment the skill is invoked, before the model sees the file, with their output spliced into the prompt. This is a documented feature called dynamic context injection, and it is genuinely useful — a skill can pull in a live git diff or the current pull request so the agent reasons about real state instead of guessing.

The exposure is the update path. A skill can be changed by a git pull or by a skill manager, and a changed skill can swap in different commands. Breunig’s own experimentation found that Claude Code has a preprocessor doing some checks — it will not run commands containing multiple instructions, for instance — but that the commands which do run execute without a permission prompt, whether or not you passed --dangerously-skip-permissions. The behavior is not well documented, and what the preprocessor screens for is not published.

drskill treats this the same way it treats MCP tool descriptions: it makes every embedded command visible and asks you to approve a specific set. injection-shell-unreviewed is a note on first sight, listing every command it found; once you ack that baseline, any later change to the command set becomes a warning showing what was added and what was removed. injection-shell-dangerous does not wait for a baseline — it fires immediately on commands matching credential paths, credential-named environment variables, network egress, or long encoded blobs.

If you would rather not have the feature at all, Claude Code can turn it off globally with "disableSkillShellExecution": true in settings, which is most useful in managed settings where users cannot override it.

A clean scan is not a clean loadout. The limitation to internalize: Claude Code skills bundled inside installed plugins are not scanned yet. If your team distributes skills as plugins, drskill scan is reporting on a fraction of what the agent loads, and a green result says nothing about the rest. .claude/commands/ directories use the same invocation-time shell syntax as skills and are not discovered either, so their embedded commands are invisible to both injection-shell-* checks.

Injection checks flag surfaces, not intent. Static analysis cannot prove a skill benign or hostile. A finding is not an accusation, and a clean scan is not a security guarantee. This is why every injection finding quotes the exact lines it judged — the verdict is yours. Bundled files that are binary or larger than 1 MiB are recorded but not content-scanned, and the report says so when that happens. Use this alongside a pre-install scanner, not instead of one.

The description checks are heuristics. Thresholds are tuned to stay quiet on well-written skills, but opposing-imperatives matches deliberately strictly and will miss paraphrased conflicts, and description-overlap will flag judgment calls. --deep exists precisely for this: it sends each flagged pair to a model to judge whether the skills are genuinely distinct, and when every pair in a cluster comes back distinct the warning downgrades to a note that no longer fails --ci.

Deep checks and connections are opt-in for a reason. --deep sends skill names and descriptions to a model provider — nothing else, and nothing at all without the flag. --mcp-connect launches and handshakes with every configured server to enumerate its tools; it never calls a tool and never reads resources or prompts, but it is still a connection you are choosing to make. Know which one you turned on.

The ledger and the cache are unsigned. Anyone who can commit to the repository can silence a warning by editing drskill.toml or by adding a verdict to .drskill/cache/. Review changes to both the way you review changes to code, or the gate becomes decorative.

A skill with an active injection finding never gets the overlap downgrade. Its pairs are still judged and the verdicts still print as evidence, but the warning stays a warning. If you are wondering why one cluster downgraded and another did not, this is usually why.