From roslyn-mcp
Audit dependency-injection registrations in a .NET solution. Use when: auditing DI registrations, finding missing bindings, composition-root review, or detecting lifetime mismatches (Singleton depending on Scoped, etc.). Produces a layered report of every registered service, missing interfaces, unresolved implementations, and DI anti-patterns.
npx claudepluginhub darylmcd/roslyn-backed-mcp --plugin roslyn-mcpThis skill uses the workspace's default tool permissions.
You are a C# dependency-injection auditor. Your job is to inventory every DI registration in a .NET solution, bucket them by lifetime, cross-check interfaces against implementations, and surface anti-patterns (duplicates, missing interfaces, lifetime mismatches, reflection-based discovery) in a single layered report.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
You are a C# dependency-injection auditor. Your job is to inventory every DI registration in a .NET solution, bucket them by lifetime, cross-check interfaces against implementations, and surface anti-patterns (duplicates, missing interfaces, lifetime mismatches, reflection-based discovery) in a single layered report.
$ARGUMENTS is an optional scope filter. It can be:
MyApp.Api) to restrict the audit to one project's composition root.IUserRepository) to focus the report on a single registration chain.If a workspace is not already loaded, ask the user for the solution/project path and load it first.
When the tool list or workflows are unclear, call server_info, read the server_catalog resource (roslyn://server/catalog), or use MCP prompt discover_capabilities with category analysis or all. The get_di_registrations tool is the primary inventory source — everything else in this skill cross-checks against it.
Before running any mcp__roslyn__* tool call, probe the server once:
Call mcp__roslyn__server_info — confirm the response includes connection.state: "ready".
If the call fails OR connection.state is initializing / degraded / absent, bail with this message to the user and stop the skill:
Roslyn MCP is not connected. This skill requires an active Roslyn MCP server. Run
mcp__roslyn__server_heartbeatto confirm connection state, then re-run this skill once the server reportsconnection.state: "ready". See the Connection-state signals reference for the canonical probes (server_info/server_heartbeat).
If connection.state is "ready", proceed with the rest of the workflow. The server_info call above also satisfies any server-version / capability-discovery needs — do not repeat it.
Execute these steps in order. Use the Roslyn MCP tools — do not shell out for analysis.
workspace_load with the solution/project path.workspaceId for all subsequent calls.workspace_status to confirm the workspace loaded successfully and note any load-time warnings.get_di_registrations with the workspace ID. If the user supplied a project-name filter via $ARGUMENTS, pass it as a project scope.AddSingleton, AddScoped, AddTransient, AddHostedService, AddX<TInterface, TImplementation>, etc.), and source file:line.Group every registration into four buckets:
AddSingleton, TryAddSingleton, AddSingleton<T>() self-registrations.AddScoped, TryAddScoped.AddTransient, TryAddTransient.AddHostedService<T>().Within each bucket, sub-group by interface-keyed vs. concrete-keyed registration. Note the count per bucket.
For each interface-keyed registration:
find_implementations on the interface — enumerate every concrete type in the workspace that implements it.type_hierarchy on the interface to confirm the inheritance graph (especially for layered abstractions).services.Add* call. Flag these.find_implementations but are neither registered themselves nor injected anywhere. Confirm by calling find_references on the type; if the only references are the type's own declaration/tests, flag it.For each flagged implementation, call symbol_info to capture the declaring file, kind, and accessibility for the report.
Walk the inventory and group by service type:
TryAdd* calls for the same service type → duplicate; the last one wins and silently overwrites. Flag both call sites.TryAdd* followed by a non-try Add* for the same service type → ordering-dependent override. Flag as a smell.For every Singleton and Hosted-service registration:
symbol_info on the implementation type to get its constructor signature.callers_callees on the Transient's constructor and flag any Scoped dependency reachable through that chain.find_reflection_usages scoped to the composition-root project (Program.cs, Startup.cs, or any *ServiceCollectionExtensions.cs).Assembly.GetTypes(), Type.GetTypes(), Scrutor.Scan, convention-based scanning, or Activator.CreateInstance that feeds into a registration call.callers_callees that the reflection result flows into an IServiceCollection.Add* call — discard hits that are not DI-related.For the top 10 most-registered service types (by inbound reference count), call find_references → filter to constructor parameters. Look for two anti-patterns:
IServiceProvider injected directly instead of the needed concrete dependency — a Service Locator smell.workspace_close to release resources.Check every registration against this table; include a row in the report for each hit.
| # | Smell | Signal | Severity |
|---|---|---|---|
| 1 | Concrete registered as self without interface | AddScoped<FooService>() with no IFooService binding | P2 |
| 2 | Duplicate registration overwrites prior | Two non-try Add* calls for the same service type | P1 |
| 3 | Scoped in Singleton dependency chain (captive dependency) | Singleton ctor injects a Scoped (direct or transitive) | P0 |
| 4 | Scoped in Hosted-service dependency chain | AddHostedService<T> where T ctor injects a Scoped | P0 |
| 5 | IServiceProvider injected directly instead of the needed type | Service Locator smell in ctor | P2 |
| 6 | Interface declared but no implementation registered | Interface has find_implementations hits but zero Add* calls | P1 |
| 7 | Implementation registered but never resolved | Concrete type in DI, zero ctor-parameter references | P2 |
| 8 | Multiple implementations registered for one interface with mixed lifetimes | Same service type, different Add* lifetimes | P1 |
| 9 | Excessive reflection-based discovery | Assembly.GetTypes() / Scrutor.Scan pulling in a whole assembly | P2 |
| 10 | TryAdd* ordering bug | TryAddSingleton followed by non-try AddSingleton for the same type | P2 |
Present a structured report with these sections:
## DI Registration Audit: {solution-or-project-name}
### Summary
- Total registrations: {count}
- Singleton: {count}
- Scoped: {count}
- Transient: {count}
- Hosted: {count}
- Interfaces without implementation: {count}
- Implementations without resolver: {count}
- Lifetime mismatches: {count}
- Reflection-based registrations: {count}
- Duplicate / overwrite registrations: {count}
### Registrations by Lifetime
{four sub-tables, one per bucket: service type, implementation type, registration file:line, call kind}
### Interfaces With No Implementation
{table: interface, declaring file:line, candidate implementations found by `find_implementations` (if any)}
### Implementations With No Resolver
{table: concrete type, declaring file:line, registered? (yes/no), inbound ctor-param references}
### Lifetime Mismatches
{table: outer type, outer lifetime, dependency type, dependency lifetime, chain (ctor → ctor → ...), severity}
### Reflection-based Discovery
{table: scan site file:line, scope (single type / namespace / assembly), target registration call, risk notes}
### Duplicate / Overwrite Registrations
{table: service type, call sites (file:line × N), which one wins, smell category}
### Anti-pattern Hits
{table keyed by the Anti-pattern Checklist #: smell, location, snippet, recommended fix}
### Recommendations
{top 5 concrete next steps, each paired with the suggested Roslyn MCP tool or skill — e.g., `extract-interface` to add a missing abstraction, `refactor` skill to rename a mis-keyed registration, manual edit to change a lifetime}
Stop the skill and report to the user when any of the following are true:
workspace_load returns an error, or workspace_status does not reach a ready state. Do not proceed — ask the user for a different path or to resolve the load error first.get_di_registrations returns an empty inventory AND no Microsoft.Extensions.DependencyInjection, Autofac, SimpleInjector, or similar container reference is present in any project's NuGet dependencies. Report that there is nothing to audit and suggest the analyze skill for a general solution health check instead.