ILens

MCP server for inspecting .NET assemblies · version 1

Contents

Why ILens

An AI agent inspecting a .dll typically falls back to ildasm (IL dump) or ilspycmd (full C# decompile) shelled out via Bash. Each invocation dumps an entire file as text, and that output stays in the agent's context as input tokens on every subsequent turn — quickly thousands of tokens spent inspecting a single class.

For a reference assembly of roughly 2 MB containing about 500 public types, the per-task cost looks like this:

TaskildasmilspycmdILensILens tokens
List every public type~150,000~120,000list_types~500
API surface of one mid-sized class~150,000~120,000summarize_type~300
Find which type exposes a method~150,000~120,000search_types~200

Figures are illustrative, not measured.

ILens turns assembly inspection into bounded, targeted lookups instead of full-file dumps — which is what makes it cheap enough to leave registered as an always-available tool.

Overview

ILens is an MCP server that speaks stdio and exposes nine tools across three categories: discovery (locating types and methods), inspection (reading API surface and cross-references), and decompilation (full C# source for a type or method). Trust posture: read-only, scoped to --allow-root directories, no network access, single self-contained binary.

Installation

Extract the release ZIP anywhere on disk. The archive contains:

No .NET runtime install is required — the binary bundles its own. Windows x64 only; the publish RID is win-x64.

Integrating with Claude Code

Two equivalent ways to register the server. Pick one.

The claude mcp add CLI command

claude mcp add ilens "C:\path\to\ILens.exe" --allow-root "C:\path\to\dlls"

A .mcp.json snippet in the project root

{
  "mcpServers": {
    "ilens": {
      "command": "C:\\path\\to\\ILens.exe",
      "args": [
        "--allow-root", "C:\\path\\to\\dlls"
      ]
    }
  }
}
Heads up. Claude Code only registers MCP servers at session start. Editing .mcp.json inside a running session will not register the new server in that session. Start a new session (menu: "New session"). Restarting just the IDE or CLI window is not sufficient — the same conversation persists across the restart.

Each --allow-root flag adds a directory tree from which assemblies may be loaded. Multiple flags are accepted. Without any roots, the server cannot load any assemblies.

Integrating with Claude Desktop

Claude Desktop reads its MCP configuration from %APPDATA%\Claude\claude_desktop_config.json. Add a mcpServers entry equivalent to the Claude Code one:

{
  "mcpServers": {
    "ilens": {
      "command": "C:\\path\\to\\ILens.exe",
      "args": [
        "--allow-root", "C:\\path\\to\\dlls"
      ]
    }
  }
}

Restart Claude Desktop after editing this file for the new server to register.

Project-level guidance

Registering ILens makes the tools available. In practice, models will still reach for ildasm via Bash, read raw .dll files, or web-search for source unless the consuming project nudges them toward ILens. A short snippet in the project's CLAUDE.md is what closes that gap.

Drop the following block into your project's CLAUDE.md and replace <allow-root> with the directory configured in .mcp.json:

## Inspecting .NET assemblies

Assemblies under `<allow-root>` are reachable through the `ilens` MCP server.
Prefer ILens tools over running `ildasm` / `ilspycmd` via Bash, reading `.dll`
files directly, or web-searching for source.

- Discovery: `search_types` (substring match), `list_types` (whole namespace),
  `find_methods` (signature search).
- Reading: `summarize_type` (public surface, no bodies), `list_members`
  (filtered surface), `decompile_type` (full C#), `decompile_method` (single
  method body).
- Cross-references: `analyze` with `kind` set to one of `UsedBy`,
  `InstantiatedBy`, `ExposedBy`, `ExtensionMethods`, `AppliedTo`,
  `OverriddenBy`, `ImplementedBy`, `Uses`, `Implements`, `ReadBy`,
  `AssignedBy`. Valid kinds depend on the symbol category.

What each line is doing

Claude Desktop has no per-project CLAUDE.md. The equivalent guidance goes into the Project's instructions or the first message of a conversation.

Security model

Tool reference

Nine tools, grouped by purpose. Every tool is read-only; every tool that takes an assembly parameter validates it against the configured allow-roots.

Discovery

find_methods read-only

Search the assembly for methods matching any combination of name, return type, parameter shape, declaring location, and accessibility. Type patterns match by short name or full name; generics are erased at the top level (List matches List<T>); Nullable<T> is unwrapped (int matches int?); arrays use a [] suffix.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
namePattern optional string
Case-insensitive substring filter on the method name.
returns optional string
Return-type pattern (e.g. bool, IEnumerable, a fully qualified name).
parameterTypes optional string[]
Ordered list of parameter-type patterns. The method must have exactly this many parameters, each matching at its position.
parameterCount optional int?
Exact parameter count. If parameterTypes is also set, the two must agree.
declaringNamespace optional string
Exact declaring-namespace filter.
declaringTypePattern optional string
Case-insensitive substring filter on the declaring type's short name.
accessibility optional AccessibilityFilter
One of Public, PublicProtected (default), All.
limit optional int?
Cap on result lines. Default 50.

Returns

A header line with the total match count followed by one line per method, each formatted as declaringType.methodName(parameters) → returnType. Sorted by declaring type, then method name. Constructors and property/event accessors are excluded; operator methods (op_*) are included. If the cap is exceeded, a trailing ... (N more matches; raise limit to see all) line is appended. With no matches, returns (no methods match).

Errors

See also

search_types for type-name search, list_members for one type's members, analyze for callers/uses of a specific method.

list_allowed_roots read-only

List the directories from which assemblies can be loaded. Useful for an agent to discover what's reachable before issuing a path-bound call.

Parameters

None.

Returns

One configured root per line. With no roots configured, returns the literal string No allowed roots configured. The server cannot load any assemblies.

Errors

None reachable from this tool.

See also

Every other tool's assembly parameter must point inside one of these roots.

list_types read-only

List all types in a namespace, by exact namespace match. Returns fully qualified type names sorted alphabetically.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
namespaceName required string
Namespace to list types from (exact match, case-sensitive).

Returns

A count header followed by one fully qualified type name per line. With no matches, returns No types found in namespace '<name>'.

Errors

See also

search_types for substring search across namespaces, summarize_type for one type's API surface.

search_types read-only

Search for types by case-insensitive substring on the short name (the namespace is not searched). Returns up to 50 matches.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
pattern required string
Search pattern matched against the type short name.

Returns

A count header followed by up to 50 fully qualified type names, sorted alphabetically. If more than 50 types match, a trailing ... (truncated, more than 50 matches) line is appended. With no matches, returns No types matching '<pattern>'.

Errors

See also

list_types when the namespace is known, find_methods for method-shape search.

Inspection

analyze read-only

Run cross-reference analysis on a type or member. The set of valid kind values depends on the symbol category that the type or member resolves to.

Valid kinds by symbol category

AppliedTo is meaningful only on attribute-derived types — a non-attribute type returns an empty result, not an error.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
typeName required string
Fully qualified type name. Nested types accept either Outer+Inner or Outer.Inner.
kind required AnalysisKind
One of the analysis kinds listed above.
memberName optional string
Member name. Omit (or pass empty string) to analyze the type itself. Resolution tries method, then property, then field, then event.
parameterCount optional int?
Method overload disambiguator (only meaningful when memberName resolves to a method).
limit optional int?
Cap on result lines. Default 50.

Returns

A header line of the form <Header> — <symbol> [origin]: followed by formatted result symbols, one per line. The origin tag indicates whether the resolved member was declared, inherited, or an extension method.

Errors

See also

decompile_method to read the body of a method analyze reports as a caller; list_members to find candidate memberName values.

list_members read-only

List members of a type grouped by kind (Methods, Properties, Fields, Events). One signature per line, no method bodies, no XML doc. Filter by kind, accessibility, and name; optionally walk base types up to System.Object. Cheaper than summarize_type when only part of the surface is needed.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
typeName required string
Fully qualified type name.
kinds optional MemberKind[]
Subset of Method, Property, Field, Event. Omit to include all four.
accessibility optional AccessibilityFilter
One of Public, PublicProtected (default), All.
namePattern optional string
Case-insensitive substring filter on the member name.
includeInherited optional bool
Walk base types up to (but not including) System.Object. Default false. Inherited members carry an [from <BaseType>] tag; overrides hide their bases.
limit optional int?
Cap on total result lines across all kinds. Default 100.

Returns

The type's full name, then one section per kind that has matches (Methods (N):, etc.) with one signature per line. If the cap truncates the output, a trailing ... (truncated, N more members; raise limit to see all) line is appended. With no matches, returns <type>: no members match the filter.

Errors

See also

summarize_type for the full public surface in C# syntax; find_methods for cross-type method search.

summarize_type read-only

Public and protected API surface of a type, rendered as C#-shaped declarations with method bodies stripped. Decompiles the type, then walks the syntax tree removing non-public members and replacing bodies with semicolons. Interface members are kept regardless of modifiers.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
typeName required string
Fully qualified type name.

Returns

A multi-line C# block: the type declaration with its surviving members, each method or constructor terminated by ; instead of a body. Property and indexer accessors are present but empty. Destructors are removed.

Errors

See also

list_members for filtered or partial surface; decompile_type for the full implementation; decompile_method for one method body.

Decompilation

decompile_method read-only

Decompile a single method to C# source. Faster and more focused than decompile_type when only one method is needed.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
typeName required string
Fully qualified type name that declares (or inherits) the method.
methodName required string
Method name.
parameterCount optional int?
Number of parameters, used to disambiguate overloads. Same-arity overloads return the first match.

Returns

A header comment of the form // <Type>.<Method> [origin] followed by the decompiled method source. The origin tag is present only when the method was found through inheritance or as an extension method.

Errors

See also

analyze with kind=UsedBy for callers; summarize_type for the surrounding type's API.

decompile_type read-only

Decompile a whole type to C# source, including all member implementations. Use this only when the full body is needed; the response can be large.

Parameters

assembly required string
Path to the .NET assembly to inspect (must be under an allowed root).
typeName required string
Fully qualified type name.

Returns

The full C# source of the type as one string.

Errors

See also

summarize_type for signature-only output; decompile_method when only one method body is needed.

Troubleshooting

Errors surface as Error: <ExceptionType>: <Message>. Common cases:

No allowed roots configured. The server cannot load any assemblies.
The server was launched without any --allow-root flags. Add at least one in .mcp.json (or via claude mcp add) and start a new session.
--allow-root path does not exist: <path>
A configured root directory does not exist on disk. Fix the path in the MCP configuration.
Path is not under any allowed root: <path>. Allowed roots: ...
The requested assembly path normalizes outside every configured root. Either move the assembly inside an allow-root or add a new root.
Path contains '..' which is not allowed: <path>
The requested path contains a literal .. segment. Pass an absolute or already-normalized path.
Assembly path is empty.
The assembly argument was missing or whitespace.
Assembly file does not exist: <path>
The path validates against the allow-roots but no file is at that location.
Assembly exceeds 200 MB size cap (actual: N MB): <path>
The file is over the hard cap. There is no flag to raise this; trim the assembly or load a slimmer one.
Type not found: <name>
The fully qualified name does not match any type. Try search_types with a substring; for nested types, use Outer+Inner (. also works).
Method '<name>' not found on <type>, its base types, or as an extension method
The method name is not declared, inherited, or available as an extension. list_members shows what is on the type.
'<method>' on <context> has N overloads. Specify parameterCount to disambiguate: ...
Two or more overloads share the name. Pass parameterCount; same-arity overloads still match the first found, by parameter count only.
No overload of '<method>' on <context> with N parameters. Available: ...
The supplied parameterCount matches none of the overloads. The error lists the available signatures.
Analysis kind 'X' is not valid for <Category>. Valid kinds for <Category>: ...
The kind argument does not apply to the resolved symbol category. The error lists the kinds that do.
parameterCount (N) contradicts parameterTypes.Length (M)
From find_methods: both are set but disagree. Drop one or align them.
Empty analyze result on an attribute target
AppliedTo is the only kind that runs on a non-method symbol but only fires when the type derives from System.Attribute. A non-attribute target returns an empty list, not an error.
Long pause on first call to a fresh assembly
The decompiler is single-threaded per assembly and the first decompile triggers full type-system load. Subsequent calls are fast. The LRU cache holds five assemblies; loading a sixth evicts the least-recently-used one (logged to stderr).

Version, source, license