A tool for inspecting .NET libraries and NuGet packages. It is for .NET what
docker inspect and kubectl describe are for containers: view package
metadata, API surfaces, dependencies, and source.
The .NET ecosystem uses a standardized binary format for managed assemblies
(ECMA-335
.dll files). That's why NuGet packages primarily distribute binaries instead
of source. That's where dotnet-inspect fits. It does for .NET binaries what
LSP-based tools do for source. dotnet-inspect reads .NET binaries to answer
basic questions about types and members and unlocks deeper insights, like call
graphs and seeing what really changed across two binary versions
dotnet tool install -g dotnet-inspect
dotnet-inspect <command>Run without installing:
dnx dotnet-inspect -y -- <command>Published tool users can install or run dotnet-inspect with the commands
above. Contributors building this repository should use the current .NET 11
preview SDK.
Check the selected SDK first:
command -v dotnet
dotnet --versionBuild from source:
dotnet build dotnet-inspect.slnx -c ReleaseSee AGENTS.md for contributor workflow, targeted test commands, and repository-specific guidance.
| Source | Examples | Notes |
|---|---|---|
| NuGet packages | package System.Text.Json, type --package Markout |
Supports versions, custom sources, nuget.config, TFMs, package layout, dependencies, and vulnerabilities. |
| Restored projects | type Command --project ./src/dotnet-inspect, project ./src/dotnet-inspect -S Skills --print |
Uses an existing project.assets.json as restored-assets context for API lookup, relationship search, and dependency package skills; restore/build first if dependencies changed. dotnet-inspect does not restore or build. |
| Platform libraries | library System.Private.CoreLib, library System.Text.Json --version 10.0.0, diff --platform System.Runtime@9.0.0..10.0.0 |
Resolves installed SDK/runtime assemblies, including runtime-only implementation assemblies with no NuGet package. |
| Local assets | library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll, package ./artifacts/MyLib.nupkg |
Useful for auditing local builds before publishing. |
Windows Metadata (.winmd) is not a supported input format, and rejection is
only partially enforced. Directory and package scans select *.dll, so a
.winmd beside them is skipped without comment. A .winmd named explicitly —
library ./Foo.winmd, find --library ./Foo.winmd — is rejected rather than
inspected, though not every surface names the reason: find reports
"Windows Metadata is not a supported metadata format", while library reports
only "Could not read library". Owners that have not yet adopted the admission
contract do not classify at all. Treat any Windows Metadata result as
unsupported output regardless of how confident it looks. Tracked by
#5559.
Bare names are routed automatically: platform-looking names (System.*,
Microsoft.AspNetCore.*) resolve to installed platform libraries; other names
resolve as NuGet packages. In API commands, common CoreLib aliases and simple
type names such as string, int, DateTime, and Guid resolve to
System.Private.CoreLib. Use explicit commands and --package, --platform,
or --library when you need a specific source.
Use -D --schema to inspect the syntax-selected structural view without
acquiring or loading the target. Package --library and --all-libraries
queries expose their route-specific schemas before package resolution, while
ambiguous commandless or dotted-member targets return separately labeled
alternatives rather than a lookup-chosen union. A commandless
<target> --all-libraries gesture always selects the package aggregate view.
Discover the stable IDs accepted by body queries:
dnx dotnet-inspect -y -- vocabulary -S "C# Body Kinds" \
--columns "ID;Label" --rows 5 --tableThen use one as a typed predicate. Kind=... auto-selects Body Shapes, while
ordinary section query options still control columns and rows:
dnx dotnet-inspect -y -- library System.Text.Json \
--where "Kind=ObjectCreationExpression" \
--columns "Member;Token;Match" --rows 3# System.Text.Json.dll
## Body Shapes
| Member | Token | Match |
| ------ | ----- | ----- |
| `System.Text.Json.JsonDocument.RootElement~2810741072:1` | `0x06000260` | `new JsonElement(this, 0)` |
| `System.Text.Json.JsonDocumentOptions.CommentHandling~4fc3b6f99d:2` | `0x060002DC` | `new ArgumentOutOfRangeException("value", SR.JsonDocumentDoesNotSupportComments)` |
| `System.Text.Json.JsonElement.GetProperty~b07c7787dc` | `0x060002EA` | `new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, propertyName))` |
For an overview, explicitly select Body Shape Summary to group identical
rendered matches with an occurrence count:
dnx dotnet-inspect -y -- type StringBuilder --platform System.Private.CoreLib \
--where "Kind=ObjectCreationExpression" -S "Body Shape Summary" \
--columns "Match;Count"Body Shapes retains individual occurrences. Keep Member, Token, and the
start/end line and column fields to locate each match in the method's rendered
C# body; these coordinates are not IL offsets or original source locations.
Hiding columns never groups rows. Summary row limits select groups without
reducing their occurrence counts; --count counts the selected view's rows.
Both views are available on library, type, and member.
Use --jsonl for one machine-readable row per match or --count for the row
count. Bodies that cannot be reconstructed at full fidelity are reported on
stderr rather than mixed into structured output.
| Capability | Commands | Highlights |
|---|---|---|
| Package inventory | package |
Metadata, versions, TFMs, file layout, dependency tree, vulnerability data, custom feeds, and NuGet config support. |
| Project package skills and docs | project |
Direct dependency Skills rows from valid package skills/**/SKILL.md files plus version-resolved package docs in a restored project context. Skill inventory values and complete documents that require containment become [Text omitted: required containment]. |
| Query vocabulary | vocabulary |
Product-owned stable values, operators, defaults, and applicability for rich queries. |
| Library audit | library |
Assembly identity, public key token, trim/AOT metadata, unsafe/interoperability signals, SourceLink, PDBs, references, resources, async methods, and body-shape search. |
| API and package discovery | type, member, find |
Type search, member tables, docs, overload selection, generics, direct calls/callers, source, decompiled C#, IL, and package-prefix discovery. |
| API compatibility | diff |
Package, platform, and library diffs with breaking/additive classification plus opt-in C#/IL and selected-member authored-source evidence. |
| Timeline correlation | timeline |
Correlate API or member-body Findings across a package version range, with evaluation and transition views. |
| Implementation matching | match |
Identity-agnostic structural equivalence for two unambiguously named methods, plus --similar seeded discovery that ranks structural candidates for one seed. |
| Relationships | graph, depends, extensions, implements |
Integration graphs, type hierarchies, package dependencies, reference graphs, extension methods/properties, implementors, and subclasses. |
| Direct dependency evidence | dependency-evidence |
One normalized snapshot of the direct dependencies declared by named package, nuspec, restored-project, or package-prefix roots, with framework scopes, version constraints, restored resolution evidence, and root-set completion. Unlike depends, it does not walk the transitive tree. |
| Source mapping | library/package -S "SourceLink: Files", type -S "Source Files", member -S "Source Locations" / "PDB Source" |
SourceLink URLs, member file/line locations, and token+IL-offset to source-line resolution. PDB Source is checksum-verified source acquired from the PDB-recorded local path, a caller-supplied Git clone (--repo), or remote SourceLink, in that order. |
| Performance analysis (experimental) | library -S @Performance, type/member -S "Performance Triage", "Top Leverage", "Resource Triage", "Call Graph" |
Whole-assembly leverage ranking, actionable rewrite-shape detection, and exception-path resource-lifecycle candidates. |
| Decompiler (experimental) | member -S @Source, member -S "Fidelity Causes", member/type/library --where "Kind=<ID>" |
Decompiled C#, annotated source, IL, body-shape queries, and typed DEC#### fidelity causes. |
| Raw metadata | library -S @Metadata, --heap "#Strings:0x1a4" |
Decoded ECMA-335 metadata tables and heap addressing. |
| Workspace scope | workspace --package X --tfm TFM |
Publish and render one complete product-owned Scope snapshot. Repeat --package to compose the Workspace; exact duplicate Roots coalesce in first-request order. |
| Workspace sharing | workspace-state encode / decode |
Convert the canonical browser/CLI base64url workspace packet to or from its bounded JSON shape without acquisition or execution. |
| Agent-friendly output | global flags | Markdown by default, compact --table, normalized --tsv, --jsonl, --json, Mermaid diagrams, section/field projection, --count, and row limiting. |
| Command | Purpose |
|---|---|
package X |
Inspect NuGet metadata, versions, dependencies, TFMs, layout, and vulnerabilities. |
project [path] |
Inspect restored project package skills and package docs. |
library X |
Inspect assembly metadata, symbols, SourceLink, references, resources, async methods, and rendered body shapes. |
type X |
Discover types or render a single type shape. |
member X |
Inspect members, docs, overloads, decompiled/lowered C#, rendered body shapes, checksum-verified PDB source, and IL. |
find [X] |
Search for types across packages, frameworks, projects, and local assets. Add --members (or lead the query with ., such as .Serialize) to search member names instead. Omit X with --package-prefix PREFIX to discover latest NuGet package manifests. |
diff X |
Compare API surfaces by default; opt into analysis or implementation evidence. |
timeline X |
Correlate API or member-body Findings across a package version range. |
graph integrations |
Induce extension, observed Integration, and Integration-opportunity relationships over an explicit package set. |
depends X |
Walk type, package, or library dependency graphs; can emit Mermaid diagrams. |
dependency-evidence |
Report the normalized direct dependencies declared by explicitly named --package, --nuspec, --project, or --package-prefix roots. Reports declarations and restored resolution evidence for those roots only; use depends to traverse. |
extensions X |
Find extension methods and C# extension properties for a type. |
implements X |
Find concrete implementors or subclasses. |
match A B |
Compare two unambiguous Type.Member names by identity-agnostic structural equivalence; add --body for decompiled C# and IL body differences. |
match A --similar |
Rank structural candidates for one seed method, within a single assembly. Ranks candidates only; it establishes no relation. |
vocabulary |
Discover product-owned query vocabularies such as Accessibility, C# Style Choices, and C# Body Kinds. |
workspace |
Render the committed ordered package Roots of one runtime Workspace, including packages with no compile assemblies. Repeat --package ID@VERSION coordinates and supply --tfm; omit packages for a typed empty Workspace. |
workspace-state encode / decode |
Convert validated workspace-state JSON and canonical base64url packets; pass - for stdin or use --file. |
skill |
Print the base LLM skill and route to focused built-in guidance (skill list, skill query, skill decompiler, skill relationships, and more). |
demo [id] |
List or run product-home inspection demos backed by real section output. |
cache |
Inspect or clear dotnet-inspect caches. |
Signals is an evidence report, not a safety certification. Use -S Signals
for a compact package or library overview, then opt into deeper audits only when
you need them.
Integration support is exposed through @Integrations or focused
Integration: ... sections such as Integration: Logging or
Integration: OpenTelemetry.
All integrations are enabled by default. Discover the supported ecosystem
predicate with library -Q Integrations, then narrow the ordinary result:
dotnet-inspect library -Q Integrations
dotnet-inspect library Aspire.Hosting.Redis@13.5.3 --tfm net8.0 -S Integrations --where "ecosystem=ecosystem.aspire"
dotnet-inspect library ./MyLibrary.dll -S "Integration: Aspire" --where "ecosystem=ecosystem.aspire" --jsonlThis filters Integration evidence and opportunities, not assembly-wide presence or Census. Other query families cannot be combined with the ecosystem predicate.
For deeper how-to guidance, use the embedded skills instead of relying on a very long README:
dotnet-inspect skill list
dotnet-inspect skill query
dotnet-inspect skill signals
dotnet-inspect skill decompiler
dotnet-inspect skill performance
dotnet-inspect skill relationshipsThese features are under active development. Their output shapes, section names, and signal sets may change between releases.
Use library -S @Performance for a whole-assembly triage pass, Top Leverage
for ranking, Resource Triage for exception-path pool-churn candidates, and
Call Graph to drill one selected member. The performance skill covers the full
workflow in more depth.
dotnet-inspect library System.Text.Json -S @Performance
dotnet-inspect library System.Text.Json -S @Performance --count
dotnet-inspect library System.Text.Json -S "Performance: Boxing" --json -T q
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S "Call Graph"Use member -S @Source for decompiled C#, annotated source, PDB source, source
diff, and IL. Use Fidelity Causes when a body cannot be raised faithfully.
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S @Source
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S "Fidelity Causes"
dotnet-inspect library System.Text.Json --il-offset 0x060002EA+0x0ReadyToRun and metadata sections are opt-in only. Use @ReadyToRun for the
validated image header and section directory. Use @Metadata to discover or
render decoded ECMA-335 table rows, --metadata-root r2r-manifest to inspect
the ReadyToRun manifest metadata instead of the default CLI root, and --heap
for one exact heap address in the selected root.
dotnet-inspect library System.Private.CoreLib -S @ReadyToRun
dotnet-inspect library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll -D @Metadata
dotnet-inspect library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll -S @Metadata --count
dotnet-inspect library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll -S "Metadata: TypeRef" --rows 20
dotnet-inspect library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll --heap "#Strings:0x1a4"
dotnet-inspect library System.Private.CoreLib --metadata-root r2r-manifest -S "Metadata: Image"Default output is Markdown. For compact human scanning use --table; for
machine-friendly rows use --tsv or --jsonl; for structured graphs use
--json; for plain text use --plaintext; and for diagrams use --mermaid.
Use -T q to suppress tips in script-oriented commands.
| Goal | Flags |
|---|---|
| Discover available sections and fields | -D, -D --schema |
| Discover query facets and operators | -Q on library/type/member/package/find; e.g. library -Q @Performance or type -Q "Body Shapes" |
| Select sections or categories | -S, wildcards such as -S "Async*", authored categories such as -S @Source or -S @Audit |
| Project columns/fields | --columns, --fields |
| Limit rows | --rows, -n, --head, --tail |
| Count results | --count |
| Materialize one payload | --print, --row, --value, --bare, --paths, --urls, --json-array |
| Control document verbosity | -v:q, -v:m, -v:n, -v:d |
| Control tip verbosity | -T q, -T m, -T d |
| Control package sources | --offline, --source, --add-source, --nugetconfig, --http-timeout |
--table, --tsv, and --jsonl render one section at a time, so pair them
with a concrete -S when querying sectioned output. Markdown and JSON can
represent multi-section documents.
Useful discovery and projection patterns:
dotnet-inspect library System.Text.Json -D
dotnet-inspect library -Q
dotnet-inspect type -Q "Body Shapes"
dotnet-inspect library -Q "Performance: Arrays" --json
dotnet-inspect member JsonSerializer --package System.Text.Json -D --schema
dotnet-inspect vocabulary -D
dotnet-inspect vocabulary -S "C# Body Kinds" --rows 10
dotnet-inspect library System.Text.Json -S Signals
dotnet-inspect library System.Text.Json -S @Audit
dotnet-inspect library Microsoft.Extensions.Logging.Abstractions -S Integrations
dotnet-inspect library Microsoft.Extensions.Logging.Abstractions -S "Integration: Logging"
dotnet-inspect library System.Diagnostics.DiagnosticSource -S "Integration: OpenTelemetry"
dotnet-inspect package System.Text.Json --path @readme --content --frontmatter
dotnet-inspect package Newtonsoft.Json -S "Package Info" --fields Version --value
dotnet-inspect project ./src/dotnet-inspect -S Skills --jsonl -T qdotnet-inspect package System.Text.Json
dotnet-inspect package System.Text.Json --versions -n 6
dotnet-inspect package System.Text.Json@8.0.0..8.0.5 --versions
dotnet-inspect package System.Text.Json -S Signals
dotnet-inspect package System.Text.Json -S "Signals,Audit: Artifact Text"
dotnet-inspect package System.Text.Json -S "Signals,Audit: Findings"
dotnet-inspect find --package-prefix Azure.AI -t 100 --tsvPatternless find --package-prefix PREFIX streams latest listed package
metadata and exact .nuspec manifests without downloading package archives.
-t limits packages rather than flattened dependency rows. Supplying a pattern
keeps API-search behavior and may acquire package archives:
dotnet-inspect find JsonSerializer --package-prefix System.TextAdd --where "facet=<ID>" to run the shared Package Query engine instead,
with one matched package per row and product-authored evidence. Discover the
executable IDs before constructing a query:
dotnet-inspect find -Q Packages
dotnet-inspect find --package-prefix dotnet-inspect -S Packages \
--where "facet=package.query.dotnet-tool" --candidates 5 --matches 5
dotnet-inspect find --package-prefix dotnet-inspect --package-content \
--where "facet=package.query.dotnet-tool-v2" --candidates 5 --matches 5 --jsonlRepeat --where to combine facets; the engine rejects incompatible selections.
Tool v1 and v2 are compatible alternatives. --candidates bounds candidate
work (default 200), while --matches stops after matching packages (default
100); each has a CLI maximum of 1,000. Content facets require
--package-content, which defaults to and permits at most 20 candidates.
Reached limits and partial failures are reported explicitly. --count counts
windowed matching package rows within the candidate budget and cannot be
combined with --matches. Query mode uses these bounds, not -t.
dotnet-inspect project ./src/dotnet-inspect -S Skills
dotnet-inspect project ./src/dotnet-inspect -S Skills --print --row 1
dotnet-inspect type Command --project ./src/dotnet-inspect
dotnet-inspect member Command --project ./src/dotnet-inspect -S "Member Index"
dotnet-inspect library ./artifacts/obj/ILInspector.Metadata/release/ILInspector.Metadata.dll -S SignalsFor API and relationship commands, --project means an existing
project.assets.json restored-assets context. Passing a .csproj or project
directory only locates that file; dotnet-inspect does not restore or build.
dotnet-inspect type string --shape
dotnet-inspect find JsonSerializer --platform System.Text.Json
dotnet-inspect member JsonSerializer --package System.Text.Json -m Serialize
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S @Source
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S "Finding Census" --json
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S Calls
dotnet-inspect member JsonSerializer --package System.Text.Json Serialize:1 -S Callers
dotnet-inspect type JsonSerializer --platform System.Text.Json -S "Source Files" --urls --json-array -T q
dotnet-inspect library System.Text.Json --il-offset 0x060002EA+0x0dotnet-inspect diff --package Markout@0.33.0..0.35.2
dotnet-inspect diff --platform System.Runtime@9.0.0..10.0.0 --breaking
dotnet-inspect timeline --package Markout@0.33.0..0.35.2 --type Markout.MarkoutWriterOptions --members --at all
dotnet-inspect timeline --package System.Text.Json@8.0.0..9.0.0 --type System.Text.Json.JsonSerializer --members --at alldotnet-inspect match Left.Compute Right.Compute --library ./app.dll
dotnet-inspect match Left.Compute Right.Compute --library ./app.dll --body
dotnet-inspect match Sample.Encode --similar --library ./app.dll
dotnet-inspect match Sample.Encode --similar --library ./app.dll --assembly-wide --top 10--body adds decompiled C# and IL body differences alongside the independent
structural-match result. Both methods must resolve to the same physical
assembly. A bodyless or unavailable endpoint stays visible rather than implying
equal bodies. Use Markdown (the default) or --json; body evidence is not
supported with --table, --tsv, --jsonl, or --similar.
--body --json returns a match/body envelope: the body document retains
per-producer native verdicts, physical endpoint addresses and availability,
structured C#/IL differences, diagnostics, and cleanup outcomes. Plain
match --json keeps its existing flat structural document. --body returns
nonzero on query cancellation or failed body comparison while preserving the available output;
a valid bodyless endpoint is reported as NoApplicableInput, not a body match.
The publication and CLI gates
cover these distinctions.
--similar ranks structural candidates for one seed method. It is a discovery
step, not a verdict: a rank establishes no relation, no semantic equivalence,
and no authorship or copying claim. Within one image, confirm a candidate by
re-running the pairwise form on the selected pair.
The default candidate population is the seed's declaring type. --assembly-wide
opts into whole-assembly retrieval, which costs materially more. --top bounds
rendered rows only; --json retains every candidate, per-method outcome,
blocker, and receipt regardless. --max-results and --max-methods move the
product retrieval limits themselves. In --table, --tsv, and --jsonl, the
ranked candidates are the only row shape; the seed, scope, disposition, receipt,
blockers, and disclosure are written to stderr so stdout stays single-shaped and
parseable.
Every ranked row prints a Token column holding the candidate's metadata token,
which the pairwise form accepts directly as the second operand. That keeps every
row addressable even when a name is ambiguous across overloads or property
accessors:
dotnet-inspect match 'Sample.Encode' 0x06000CF8 --library ./app.dllA metadata token is a table row index, not a portable identity, so it addresses
a member only in the assembly that defines it. match resolves a token against
the image named by --library and fails when that image does not define the
row, rather than binding it to an unrelated member.
That distinction is visible when --library names a facade. If the seed's type
is forwarded, the ranked rows come from the assembly that actually defines them,
so the run names that assembly and the exact --library value to pass when
confirming a candidate:
dotnet-inspect match System.String.IsNullOrEmpty --similar --library ./System.Runtime.dllComparing candidates drawn from two different assemblies is not supported; inspect each side on its own.
dotnet-inspect depends Stream --markdown --mermaid
dotnet-inspect dependency-evidence --package Newtonsoft.Json --tfm net8.0
dotnet-inspect dependency-evidence \
--project ./src/dotnet-inspect \
--nuspec ./artifacts/package.nuspec \
-v:n
dotnet-inspect implements IEquatable --project ./src/dotnet-inspect -v:q
dotnet-inspect extensions string --project ./src/dotnet-inspect -v:q
dotnet-inspect graph integrations \
--package Microsoft.Extensions.DependencyInjection.Abstractions@10.0.0 \
--package Microsoft.Extensions.Logging.Abstractions@10.0.0 \
--package Microsoft.Extensions.Logging@10.0.0 \
--package Microsoft.Extensions.Http@10.0.0 \
--tfm net10.0 \
--relationship integration.observeddotnet-inspect workspace-state decode "$w"
dotnet-inspect workspace-state decode "$w" | jq
dotnet-inspect workspace-state encode --file workspace-state.json
dotnet-inspect workspace-state encode --file workspace-state.json --url
dotnet-inspect skill list
dotnet-inspect demo listworkspace-state encode --url emits https://dotnet-inspect.net/?w=<packet>
for the existing share-packet JSON shape. Packet-only output remains the default.
This is not an encoder for workspace --json inventory output. The packet's
existing limits and the browser's supported restoration shapes still apply.
.NET 10.0 SDK or later.
dotnet-inspect is designed for LLM-driven development.
The embedded skill (dotnet-inspect skill) is also distributed through the
dotnet/skills marketplace.
Start with AGENTS.md for repository-wide engineering and workflow rules. Use docs/overview.md when a change crosses subsystem ownership boundaries, and taste/skill-guidance.md when maintaining the embedded skill.
MIT