agentsclimarketplace

Exception audit

Skill darylmcd/Roslyn-Backed-MCP/skills/exception-audit

Repo-wide classification of exception handling patterns. Use when: auditing exception handling, finding swallowed exceptions, classifying catch patterns, finding overly-broad catches, or mapping unhandled-at-boundary throw sites in a C# solution. Optionally takes an exception type name to filter the audit.From its SKILL.md

Install
npx -y skills add darylmcd/Roslyn-Backed-MCP --skill exception-audit

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

One thing to look at

  • 1 stars1 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.

SKILL.md

12.6 KB, as published. Nobody here has run it

Exception Handling Audit

You are a C# exception-handling auditor. Your job is to load a workspace, enumerate every catch clause reachable from known exception types, classify each catch body by pattern (swallow, rethrow-as-is, rethrow-wrapped, delegate, handle), flag throw sites with no in-solution catch, and produce a ranked classification report that feeds downstream error-handling refactors.

Input

$ARGUMENTS is an optional exception type name (e.g., IOException, HttpRequestException, MyDomain.ValidationException). If provided, the audit is scoped to catches assignable from that type and throw sites of that type. If omitted, audit every exception type in use across the solution, with special attention to System.Exception itself (the broadest catch).

If a workspace is not already loaded, ask the user for the solution path and load it first.

Server discovery

Use server_info, resource roslyn://server/catalog, or MCP prompt discover_capabilities (analysis or all) for the live tool list and WorkflowHints. The primary tool for this skill is trace_exception_flow — confirm its presence in the catalog before starting.

Connectivity precheck

Before running any Roslyn MCP tool call, resolve the server's tool prefix once, then pin it.

The prefix is client-assigned — never hard-code it. Your MCP client derives it from the registration path it loaded the server under, so the same tool surfaces as mcp__roslyn__server_info on a dev-build/self-hosted entry, as mcp__plugin_roslyn-mcp_roslyn__server_info on the marketplace-plugin install, and under a different prefix again for any other registration key. Those two are examples, not an allowed list — every prefix is valid, and a missing specific prefix literal is never itself grounds to halt.

  1. Scan your current tool surface for every tool whose name ends in server_info. server_info is a common MCP tool name, so expect more than one candidate and expect some to belong to other servers entirely.

  2. Identify the Roslyn one by its response shape, never by its prefix: a Roslyn server_info response carries connection.state, catalogVersion, and surface.*. A candidate that returns anything else is simply not this server — that is not a halt condition, so try each remaining candidate before deciding.

  3. Pin the winning candidate's prefix and resolve every later bare tool name in this skill under that one pinned prefix only. Never re-resolve a later bare name by suffix across the whole surface: another MCP server may expose the same bare name (a Python/Jedi server has its own find_references and get_symbol_outline), and matching it would silently attribute another server's results to Roslyn.

  4. Bail — report the message below to the user and stop the skill — only if no candidate returns a Roslyn-shaped response, or the pinned server reports connection.state as anything other than "ready" (initializing, degraded, absent):

    Roslyn MCP is not connected. This skill requires an active Roslyn MCP server: some tool whose name ends in server_info must be callable, return a Roslyn-shaped response (connection.state, catalogVersion, surface.*), and report connection.state: "ready". The prefix does not matter — your client assigns it from the registration path, so it may be mcp__roslyn__…, mcp__plugin_roslyn-mcp_roslyn__…, or anything else. Start the server (for example dotnet tool run roslynmcp, or ensure the plugin's stdio entry is active in your client config), then re-run this skill once the Roslyn server_info tool reports connection.state: "ready" under whichever prefix your tool surface exposes. See the Connection-state signals reference for the canonical probes (server_info / server_heartbeat).

  5. Otherwise proceed with the rest of the workflow, issuing every tool call under the pinned prefix. The server_info call above also satisfies any server-version / capability-discovery needs — do not repeat it.

Workflow

Execute these steps in order. Use the Roslyn MCP tools — do not shell out or grep the source tree for catch clauses.

Step 1: Load Workspace

  1. Call workspace_load with the solution/project path. Store the returned workspaceId for all subsequent calls.
  2. Call workspace_status to confirm the workspace loaded successfully and note any load-time warnings. If the load fails, see Refusal conditions below.

Step 2: Seed the Exception Type Set

  1. If the user supplied a type filter in $ARGUMENTS, use symbol_search to resolve it to a fully-qualified type symbol. Confirm with symbol_info. If ambiguous, show candidates and ask the user to pick.
  2. If no filter was supplied, seed the set with:
    • System.Exception (broadest catch — always audit).
    • Every exception type discovered by symbol_search for names ending in Exception inside the solution's source projects.
    • Framework exception types surfaced via find_type_usages on throw sites (see Step 4).

Step 3: Enumerate Catch Clauses (primary)

For each exception type in the seed set:

  1. Call trace_exception_flow with the exception type. This returns every catch clause assignable from that type, including:
    • File / line / enclosing method (use enclosing_symbol to confirm when the response lacks it).
    • The catch clause's declared exception type (may be a base type).
    • A body excerpt.
    • Rethrow-as annotations (throw, throw new X(...), throw; — i.e., bare rethrow — and any wrapping chain).
  2. Accumulate the catch clauses into a single table keyed by (file, line, enclosingSymbol).
  3. Separately, use semantic_search with queries such as "catch (Exception" and "catch (" to surface any catches the exception-flow trace missed (e.g., variable-less catches, filter expressions). Merge new entries into the accumulator.

Step 4: Enumerate Throw Sites

For each exception type in the seed set:

  1. Use symbol_search (kind Method / Constructor) plus find_references on the exception type's constructors to locate throw sites across the solution.
  2. Record (file, line, enclosingSymbol, exceptionType) for each throw new X(...) and bare throw; outside a catch.

Step 5: Classify Each Catch Body

For every catch clause captured in Step 3, classify the body using this procedure:

  1. Call analyze_control_flow on the catch block span to determine whether it exits normally, rethrows, or returns.
  2. Read the body excerpt from trace_exception_flow (or pull the enclosing span via get_source_text / get_syntax_tree if the excerpt is truncated).
  3. Apply the Classification Rubric below to assign one of: swallow, swallow-with-log, rethrow-as-is, rethrow-wrapped, delegate, handle-and-continue, or unknown.
  4. For delegate candidates (catch body calls another method that may itself throw), use callers_callees on the enclosing method to record the propagation chain one level up. Do not recurse indefinitely — one hop is enough for the report.

Step 6: Identify Possible Unhandled-at-Boundary

For every throw site captured in Step 4:

  1. Use callers_callees on the throwing method to walk the call graph upward.
  2. At each frame, check whether an accumulated catch clause (from Step 3) covers the exception type (by assignability — use symbol_info / type_hierarchy if needed).
  3. If the walk reaches a public entry point (program Main, ASP.NET controller action, Task returned from a top-level handler, test method, etc.) without finding a covering catch, flag the throw site as possible unhandled-at-boundary. Record the entry-point frame.
  4. If the walk is cut off by recursion, virtual dispatch into unknown implementations, or a delegate boundary, mark the site unknown-boundary rather than unhandled — the classifier is conservative.

Step 7: Aggregate and Rank

Aggregate Steps 3-6 into a single ranked report:

TierSignal
P0 — CriticalEmpty catch (Exception) body (pure swallow of the broadest type); catch (Exception) that logs-and-continues at a domain boundary; unhandled-at-boundary throw of a non-recoverable exception type.
P1 — HighNon-empty catch (Exception) (broad catch) that does not rethrow; rethrow-wrapped that replaces InnerException with null; rethrow via throw ex; (stack-trace loss).
P2 — MediumTyped swallow-with-log (e.g., catch (IOException) { _log.Warn(...); }); rethrow-as-is where a typed wrapper would be more appropriate; catch with a filter expression whose branches mix swallow and rethrow.
P3 — LowTyped handle-and-continue with a sound recovery path; delegate catches whose inner method is a thin wrapper; informational / style-only findings.

Sort by tier then by frequency-of-pattern descending; break ties by file:line ascending.

Step 8: Close Workspace

Call workspace_close to release resources.

Classification Rubric

PatternSignal (what to look for in the catch body)Severity
swallowBody is empty, or contains only a comment / return; / return default; with no log or side effect. analyze_control_flow reports normal exit.P0 if catch (Exception), else P1
swallow-with-logBody is exactly: log/trace call(s) and then falls through. No rethrow, no recovery state change.P1 if catch (Exception), else P2
rethrow-as-is (safe)Body ends with bare throw;. Stack trace preserved.P3
rethrow-as-is (unsafe)Body ends with throw ex; (named variable) — overwrites the stack trace.P1
rethrow-wrappedBody ends with throw new X(..., ex); where ex is passed as innerException.P3 if wrapper is typed domain exception; P1 if innerException is dropped.
delegateBody calls another method (e.g., HandleError(ex), Policy.Handle(ex)) and the catch exits normally. Requires a one-hop callers_callees probe to resolve.P2
handle-and-continueBody mutates recoverable state (e.g., sets a fallback, returns a default value) and exits normally. Typed catch only.P3
broad-catchClause is catch (Exception) or catch (no type / no variable) regardless of body — tag every such clause in addition to its body classification.Promote the body's tier by one (min P1).
unknownControl flow or body excerpt could not be classified confidently.P2 (flag for manual review)

Output Format

Present a structured report with these sections:

## Exception Handling Audit: {solution-name}{ for {filter-type} if filtered}

### Summary
- Exception types audited: {count}
- Catch clauses found: {count}
- Throw sites found: {count}
- Possible unhandled-at-boundary: {count}
- Broad catches (`catch (Exception)` or variable-less): {count}

### Catch Population
{table — columns: Exception Type, Total Catches, Swallow, Swallow+Log, Rethrow-as-is, Rethrow-wrapped, Delegate, Handle, Unknown}

### Swallowed Exceptions
{table — columns: #, Tier, file:line, Enclosing Symbol, Catch Type, Body Excerpt (one-line), Notes}

### Broad Catches (`catch (Exception)` / variable-less)
{table — columns: #, Tier, file:line, Enclosing Symbol, Body Classification, Rationale}

### Possible Unhandled-at-Boundary
{table — columns: #, Throw Site (file:line), Exception Type, Enclosing Method, Reached Boundary, Propagation Chain}

### Recommendations
{top 3-5 refactors — e.g., "Replace `throw ex;` at X with bare `throw;` (use refactor skill)", "Narrow `catch (Exception)` at Y to `catch (IOException, TimeoutException)`", "Wrap throw site Z in a typed domain exception at the API boundary". Each recommendation cites the suggested next tool or skill (e.g., `code-actions`, `refactor`, `code_fix_preview` for a diagnostic ID).}

Refusal conditions

If workspace_load fails or workspace_status reports the workspace did not load, stop the skill and report the failure with any load-time diagnostics returned by workspace_status. Do not attempt any of Steps 2-8 without a loaded workspace — the catch-graph enumeration requires semantic symbols that are only available after a successful load.

Keep looking

Skills are one crate of 627,801. 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.