dnSpy.MCP.Server

chichicaste/dnSpy.MCP.Server

3.2

If you are the rightful owner of dnSpy.MCP.Server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

A very preliminary MCP Server extension for dnSpyEx.

dnSpy MCP Server

A Model Context Protocol (MCP) server embedded in dnSpy that exposes full .NET assembly analysis, editing, debugging, memory-dump, and deobfuscation capabilities to any MCP-compatible AI assistant.

Version: 1.7.0 | Tools: 98 | Status: beta2 | Targets: .NET 4.8 + .NET 10.0-windows


Table of Contents

  1. Features
  2. Build & Install
  3. Client Configuration
  4. Tool Reference
  5. Pattern Syntax
  6. Pagination
  7. Usage Examples
  8. Architecture
  9. Project Structure
  10. Configuration
  11. Troubleshooting

Features

CategoryCapabilities
AssemblyList loaded assemblies, namespaces, type counts, P/Invoke imports
TypesInspect types, fields, properties, events, nested types, attributes, inheritance
DecompilationDecompile entire types or individual methods to C#
ILView IL instructions, raw bytes, local variables, exception handlers
AnalysisFind callers/users, trace field reads/writes, call graphs, dead code, cross-assembly dependencies
EditRename members, change access modifiers, edit metadata, patch methods, inject types, save to disk
ResourcesList, read, add, remove embedded resources (ManifestResource table); extract Costura.Fody-embedded assemblies
DebugManage breakpoints (with conditions), launch/attach processes, pause/resume/stop sessions, single-step (over/into/out), inspect call stacks, read locals, evaluate expressions
Memory DumpList runtime modules, dump .NET or native modules from memory, read/write process memory, extract PE sections
Static PE AnalysisScan raw PE bytes for strings; all-in-one ConfuserEx unpacker
Deobfuscationde4dot integration: detect obfuscator, rename mangled symbols, decrypt strings. Both in-process (deobfuscate_assembly) and external process (run_de4dot) modes available in all builds
Window / DialogList active dialog/message-box windows (Win32 #32770 + WPF) in the dnSpy process; dismiss them by clicking any button by name (supports EN and ES)
SearchGlob and regex search across all loaded assemblies

Build & Install

Prerequisites

RequirementVersion
.NET SDK10.0+ (for net10.0-windows target)
.NET Framework SDK4.8 (for net48 target)
OSWindows (WPF dependency)
dnSpydnSpyEx (this repo)

de4dot integration — de4dot libraries are bundled in libs/de4dot/ (net48) and libs/de4dot-net8/ (net8/net10). No external dependencies required; all deobfuscation tools are available in both build targets.

Clone & Restore

git clone https://github.com/dnSpyEx/dnSpy --recursive
cd dnSpy

Build commands

# Build only the MCP Server extension (Debug)
dotnet build Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj -c Debug

# Build only the MCP Server extension (Release)
dotnet build Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj -c Release

# Build the full dnSpy solution (both targets)
dotnet build dnSpy.sln -c Debug

# Build for a specific target framework only
dotnet build Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj -c Release -f net10.0-windows
dotnet build Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj -c Release -f net48

# Restore NuGet packages without building
dotnet restore Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj

# Clean build artifacts
dotnet clean Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj

Output locations

TargetOutput path
.NET 10.0-windowsdnSpy/dnSpy/bin/Release/net10.0-windows/dnSpy.MCP.Server.x.dll
.NET Framework 4.8dnSpy/dnSpy/bin/Release/net48/dnSpy.MCP.Server.x.dll

The MCP Server DLL is output directly into dnSpy's bin directory so it loads automatically when you start dnSpy.

Verify the build

# Check for errors (expects "Compilación correcta" or "Build succeeded")
dotnet build Extensions/dnSpy.MCP.Server/dnSpy.MCP.Server.csproj -c Release --nologo 2>&1 | tail -5

Runtime

  1. Start dnSpy — the MCP server starts automatically on http://localhost:3100
  2. Verify it is running:
    curl http://localhost:3100/health
    curl -N --max-time 3 http://localhost:3100/sse   # should print event: endpoint
    
  3. Configure your MCP client (see next section)

Client Configuration

The server implements the MCP SSE transport (spec version 2024-11-05). On connect the server sends an event: endpoint with the per-session POST URL; responses are pushed back over the SSE stream.

Claude Code (CLI)

claude mcp add dnspy --transport sse http://localhost:3100/sse

Claude Desktop

{
  "mcpServers": {
    "dnspy": {
      "type": "sse",
      "url": "http://localhost:3100/sse"
    }
  }
}

OpenCode

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "dnspy": {
      "type": "remote",
      "url": "http://localhost:3100/sse",
      "enabled": true
    }
  }
}

Kilo Code / Roo Code

{
  "mcpServers": {
    "dnspy-mcp": {
      "type": "sse",
      "url": "http://localhost:3100/sse",
      "alwaysAllow": [
        "list_assemblies", "list_tools", "search_types",
        "get_type_info", "list_methods_in_type"
      ],
      "disabled": false
    }
  }
}

Codex CLI

{
  "mcpServers": {
    "dnspy": {
      "type": "sse",
      "url": "http://localhost:3100/sse",
      "timeout": 30
    }
  }
}

Gemini CLI

mcpServers:
  dnspy:
    type: sse
    url: http://localhost:3100/sse

SSE endpoints: GET /sse (or /events, /) opens the event stream. The server immediately sends event: endpoint\ndata: http://localhost:3100/message?sessionId=<id>. The client then POSTs JSON-RPC requests to that URL and receives responses as event: message SSE events. POST / still accepts direct JSON-RPC for curl/scripting use.


Tool Reference

All tools are called over MCP as tools/call with a JSON arguments object. Parameters marked required must always be provided; all others are optional.


Assembly Tools

Tools for listing and inspecting .NET assemblies loaded in dnSpy.

ToolDescriptionRequired paramsOptional params
list_assembliesList every assembly currently open in dnSpy, with version, culture, and public key token
get_assembly_infoDetailed info for one assembly: modules, namespaces, type countassembly_namecursor
list_typesList types in an assembly, with class/interface/enum flags. Supports glob and regex via name_patternassembly_namenamespace, name_pattern, cursor
list_native_modulesList native DLLs imported via [DllImport], grouped by DLL name with the managed methods that use themassembly_name
load_assemblyLoad a .NET assembly into dnSpy from a file on disk or from a running process by PID. Supports both normal PE layout and raw memory-layout dumpsfile_path, memory_layout, pid, module_name
select_assemblySelect an assembly in the dnSpy document tree and open it in the active tab; changes the current assembly context for subsequent operationsassembly_namefile_path
close_assemblyClose (remove) a specific assembly from dnSpyassembly_namefile_path
close_all_assembliesClose all assemblies currently loaded in dnSpy, clearing the document tree
Parameter details
ParameterTypeDescription
assembly_namestringShort assembly name as shown in dnSpy (e.g. UnityEngine.CoreModule)
namespacestringExact namespace filter (e.g. System.Collections.Generic)
name_patternstringGlob or regex filter on type name — see Pattern Syntax
cursorstringOpaque base-64 pagination cursor from nextCursor in a previous response
file_pathstring(load_assembly) Absolute path to a .NET assembly or memory dump
memory_layoutboolean(load_assembly) When true, treat the file as raw memory-layout (VAs, not file offsets). Default false
pidinteger(load_assembly) PID of a running .NET process to dump from. Requires active debug session.
module_namestring(load_assembly) Module name/filename to pick when using pid. Defaults to first EXE module.
file_pathstring(select_assembly, close_assembly) Absolute path (FilePath from list_assemblies) to disambiguate when multiple assemblies share the same short name

Type & Member Tools

Tools for inspecting the internals of a specific type.

ToolDescriptionRequired paramsOptional params
get_type_infoFull type overview: visibility, base type, interfaces, fields, properties, methods (paginated)assembly_name, type_full_namecursor
search_typesSearch for types by name across all loaded assembliesquerycursor
get_type_fieldsList fields matching a name pattern, with type, visibility, and readonly/const flagsassembly_name, type_full_name, patterncursor
get_type_propertyFull detail for a single property: getter/setter signatures, attributesassembly_name, type_full_name, property_name
list_properties_in_typeSummary list of all properties with read/write flagsassembly_name, type_full_namecursor
list_events_in_typeAll events with add/remove method infoassembly_name, type_full_name
list_nested_typesAll nested types recursively (full name, visibility, kind)assembly_name, type_full_name
get_custom_attributesCustom attributes on the type or on one of its membersassembly_name, type_full_namemember_name, member_kind
analyze_type_inheritanceFull inheritance chain (base classes + interfaces)assembly_name, type_full_name
find_path_to_typeBFS traversal of property/field references to find how one type reaches anotherassembly_name, from_type, to_typemax_depth
Parameter details
ParameterTypeDescription
type_full_namestringFully-qualified type name (e.g. MyNamespace.MyClass)
querystringSubstring, glob, or regex matched against FullName
patternstringGlob or regex for field name matching; use * to list all
property_namestringExact property name (case-insensitive)
member_namestringMember name for attribute lookup (omit to get type-level attributes)
member_kindstringDisambiguates overloaded names: method, field, property, or event
from_typestringFull name of starting type for BFS path search
to_typestringName or substring of the target type
max_depthintegerBFS depth limit (default 5)

Method & Decompilation Tools

Tools for decompiling code and exploring method metadata.

ToolDescriptionRequired paramsOptional params
decompile_typeDecompile an entire type (class/struct/interface/enum) to C#assembly_name, type_full_name
decompile_methodDecompile a single method to C#assembly_name, type_full_name, method_name
list_methods_in_typeList methods with return type, visibility, static, virtual, parameter count. Filter by visibility or name patternassembly_name, type_full_namevisibility, name_pattern, cursor
get_method_signatureFull signature for one method: parameters, return type, generic constraintsassembly_name, type_full_name, method_name
Parameter details
ParameterTypeDescription
method_namestringMethod name (first match when overloads exist; use get_method_signature to disambiguate)
visibilitystringFilter: public, private, protected, or internal
name_patternstringGlob or regex on method name (e.g. Get*, ^On[A-Z], Async$)

IL Tools

Low-level IL inspection for a method body.

ToolDescriptionRequired paramsOptional params
get_method_ilIL instruction listing with offsets, opcodes, operands, and local variable tableassembly_name, type_full_name, method_name
get_method_il_bytesRaw IL bytes as a hex string and Base64assembly_name, type_full_name, method_name
get_method_exception_handlerstry/catch/finally/fault region table (offsets and handler type)assembly_name, type_full_name, method_name
dump_cordbg_ilFor each MethodDef in the paused module, reads ICorDebugFunction.ILCode.Address and ILCode.Size via the CorDebug COM API (through reflection). Reports whether IL addresses fall inside the PE image (encrypted stubs) or outside (JIT hook buffers). Useful for ConfuserEx JIT-hook analysis. Requires an active paused debug sessionmodule_name, output_path, max_methods, include_bytes
Parameter details (dump_cordbg_il)
ParameterTypeDescription
module_namestringModule name or filename filter (default: first EXE module)
output_pathstringOptional path to save full JSON results to disk
max_methodsintegerMax number of MethodDef tokens to scan (default 10000)
include_bytesbooleanWhen true, include Base64-encoded IL bytes for each method (default false)

Analysis & Cross-Reference Tools

Call-graph, usage, and dependency analysis across all loaded assemblies.

ToolDescriptionRequired paramsOptional params
find_who_calls_methodFind every method whose IL contains a call/callvirt to the targetassembly_name, type_full_name, method_name
find_who_uses_typeFind all types, methods, and fields that reference a type (base class, interface, field type, parameter, return type)assembly_name, type_full_name
find_who_reads_fieldFind all methods that read a field via ldfld/ldsfld IL instructionsassembly_name, type_full_name, field_name
find_who_writes_fieldFind all methods that write to a field via stfld/stsfld IL instructionsassembly_name, type_full_name, field_name
analyze_call_graphBuild a recursive call graph for a method, showing all methods it calls down to a configurable depthassembly_name, type_full_name, method_namemax_depth
find_dependency_chainFind all dependency paths between two types via BFS over base types, interfaces, fields, parameters, and return typesassembly_name, from_type, to_typemax_depth
analyze_cross_assembly_dependenciesCompute a dependency matrix for all loaded assemblies, showing which assemblies each depends on
find_dead_codeIdentify methods and types never called or referenced (static approximation; virtual dispatch and reflection are not tracked)assembly_name
Parameter details
ParameterTypeDescription
field_namestringExact field name to search for read/write access
max_depthintegerRecursion depth limit for call-graph or BFS traversal (default 5)

Edit Tools

In-memory metadata editing. Changes are applied immediately to dnlib's in-memory model and persist until save_assembly is called or dnSpy is closed without saving.

ToolDescriptionRequired paramsOptional params
change_member_visibilityChange the access modifier of a type or one of its membersassembly_name, type_full_name, member_kind, new_visibilitymember_name
rename_memberRename a type or one of its membersassembly_name, type_full_name, member_kind, old_name, new_name
save_assemblyWrite the (possibly modified) assembly to disk using dnlib's ModuleWriterassembly_nameoutput_path
get_assembly_metadataRead assembly-level metadata: name, version, culture, public key, flags, hash algorithm, module count, custom attributesassembly_name
edit_assembly_metadataEdit assembly-level metadata fields: name, version, culture, or hash algorithmassembly_namename, version, culture, hash_algorithm
set_assembly_flagsSet or clear an individual assembly attribute flag (e.g. PublicKey, Retargetable, processor architecture)assembly_name, flag_name, value
list_assembly_referencesList all assembly references (AssemblyRef table entries) in the manifest moduleassembly_name
add_assembly_referenceAdd an assembly reference by loading a DLL from disk. Creates a TypeForwarder to anchor the referenceassembly_name, dll_path
remove_assembly_referenceRemove an AssemblyRef entry and all TypeForwarder entries that target it. Returns a warning if TypeRefs in code still use the referenceassembly_name, reference_name
inject_type_from_dllDeep-clone a type (fields, methods with IL, properties, events) from an external DLL into the target assemblyassembly_name, dll_path, type_full_name
list_pinvoke_methodsList all P/Invoke (DllImport) declarations in a type: managed name, token, DLL name, native function nameassembly_name, type_full_name
patch_method_to_retReplace a method's IL body with a minimal return stub (nop + ret) to neutralize it. Works on P/Invoke methods too (converts to managed stub)assembly_name, type_full_name, method_name
Parameter details
ParameterTypeValues / Description
member_kindstringtype, method, field, property, or event
new_visibilitystringpublic, private, protected, internal, protected_internal, private_protected
old_namestringCurrent member name
new_namestringDesired new name
output_pathstringAbsolute path for output file. Defaults to the original file location.
flag_namestringAssembly flag to toggle (e.g. PublicKey, Retargetable, PA_MSIL, PA_x86, PA_AMD64)
valuebooleantrue to set the flag, false to clear it
dll_pathstringAbsolute path to the source DLL

Note: rename_member changes only the metadata name. It does not update call sites, string literals, or XML docs.

Note: patch_method_to_ret is ideal for disabling anti-debug, anti-tamper, or license-check routines before saving and re-analyzing.


Embedded Resource Tools

Read, write, and extract entries from the ManifestResource table. All write operations are in-memory until save_assembly is called.

ToolDescriptionRequired paramsOptional params
list_resourcesList all ManifestResource entries: name, kind (Embedded/Linked/AssemblyLinked), size, visibility, and whether it looks like a Costura.Fody-embedded assemblyassembly_name
get_resourceExtract an embedded resource as Base64 (up to 4 MB inline) and/or save to diskassembly_name, resource_nameoutput_path, skip_base64
add_resourceEmbed a file from disk as a new EmbeddedResource in the assemblyassembly_name, resource_name, file_pathis_public
remove_resourceDelete a ManifestResource entry by nameassembly_name, resource_name
extract_costuraDetect and extract Costura.Fody-embedded assemblies (costura.*.dll.compressed resources). Decompresses gzip automatically. Useful for analysing assemblies packed with Costuraassembly_name, output_directorydecompress
Parameter details
ParameterTypeDescription
resource_namestringExact resource name (use list_resources to find it)
output_pathstring(get_resource) Absolute path to write raw resource bytes
skip_base64boolean(get_resource) Omit Base64 from response; useful when saving large resources to disk (default false)
is_publicboolean(add_resource) Resource visibility — true = Public (default), false = Private
output_directorystring(extract_costura) Directory where extracted DLLs/PDBs will be written
decompressboolean(extract_costura) Decompress gzip-compressed resources (default true)

Costura.Fody workflow: list_resources (confirm costura.* entries exist) → extract_costura output_directory=C:\extractedload_assembly each extracted DLL → analyse normally with MCP tools.


Skills Knowledge Base

A persistent reverse-engineering knowledge base stored in %APPDATA%\dnSpy\dnSpy.MCPServer\skills\. Each skill is a pair of files: a Markdown narrative ({id}.md) and a JSON technical record ({id}.json). Skills capture step-by-step procedures, magic values, crypto keys, algorithms, offsets, and generic prompts — so once you've reversed a packer or obfuscator, the knowledge is reusable.

Workflow: Before analysing a new binary, call search_skills to check for existing knowledge. After completing an analysis, call save_skill to record findings.

ToolDescriptionRequired paramsOptional params
list_skillsList all skills in the knowledge base with ID, name, description, tags, and targetstag
get_skillRetrieve the full Markdown narrative and JSON technical record of a skillskill_id
save_skillCreate or update a skill. Writes Markdown and/or JSON. Use merge=true to append findings without overwriting existing dataskill_idname, description, tags, targets, markdown, json_data, merge
search_skillsFull-text search across all skill Markdown and JSON files. Returns matches with context snippetsquery or tagquery, tag
delete_skillPermanently delete a skill (both .md and .json files)skill_id
Parameter details
ParameterTypeDescription
skill_idstringSkill identifier — will be slugified (e.g. confuserex-unpacking). Use list_skills to see existing IDs
namestringHuman-readable skill name
descriptionstringShort summary of what the skill covers
tagsstringComma-separated or JSON array of tags (e.g. packer,confuserex,unpacking)
targetsstringComma-separated or JSON array of target binary names or hashes this skill applies to
markdownstringMarkdown narrative: what to do, why, key observations, procedure steps in prose
json_datastringJSON object with technical details: procedure (steps with tool/prompt/expected), magic_values, crypto_keys, algorithms, offsets, findings, prompts (identify/apply/verify/troubleshoot)
mergebooleanIf true, deep-merge json_data into the existing record instead of replacing it (default false)
querystring(search_skills) Keyword or phrase to search for in all skill files
tagstring(list_skills, search_skills) Filter results to skills containing this tag substring
Example JSON skill record structure
{
  "id": "confuserex-unpacking",
  "name": "ConfuserEx Unpacking",
  "version": "1",
  "tags": ["packer", "confuserex", "unpacking"],
  "targets": ["MyProtectedApp.exe"],
  "description": "Step-by-step procedure to unpack ConfuserEx 1.x protected assemblies",
  "procedure": [
    { "step": 1, "tool": "detect_obfuscator", "prompt": "Detect which obfuscator was applied", "expected": "ConfuserEx v1.x" },
    { "step": 2, "tool": "start_debugging",   "prompt": "Launch under dnSpy with break_kind=EntryPoint", "expected": "Paused at entry point after .cctor" },
    { "step": 3, "tool": "dump_module_from_memory", "prompt": "Dump decrypted module from RAM", "expected": "Raw PE bytes" }
  ],
  "magic_values": { "key_offset": "0x1234", "xor_key": "0xDEADBEEF" },
  "crypto_keys": [],
  "algorithms": ["XOR", "AES-128-ECB"],
  "prompts": {
    "identify": "Use detect_obfuscator on the assembly. If ConfuserEx, proceed with this skill.",
    "apply": "Follow procedure steps 1-3. After dump, reload and deobfuscate.",
    "verify": "Decompile Main() — if readable C# appears, unpacking succeeded.",
    "troubleshoot": "If dump_module_from_memory fails, try dump_module_unpacked with fix_pe_header=true."
  }
}

Debug Tools

Interact with dnSpy's integrated debugger. Most tools require an active debug session.

ToolDescriptionRequired paramsOptional params
get_debugger_stateCurrent state: IsDebugging, IsRunning, process list with thread/runtime counts
list_breakpointsAll registered code breakpoints with enabled state, bound count, and location
set_breakpointSet a breakpoint at a method entry point or specific IL offset. Supports an optional C# condition expression — only fires when the expression evaluates to trueassembly_name, type_full_name, method_nameil_offset, condition, file_path
remove_breakpointRemove a specific breakpointassembly_name, type_full_name, method_nameil_offset
clear_all_breakpointsRemove every visible breakpoint
continue_debuggerResume all paused processes (RunAll)
break_debuggerPause all running processes (BreakAll)
stop_debuggingTerminate all active debug sessions
get_call_stackCall stack of the currently selected (or first paused) thread — up to 50 frames
step_overStep over the current statement. Blocks until the step completes (or timeout). Returns the new execution location (token, IL offset, module).thread_id, process_id, timeout_seconds
step_intoStep into the next called method. Same blocking behaviour as step_over.thread_id, process_id, timeout_seconds
step_outRun until the current method returns to its caller.thread_id, process_id, timeout_seconds
get_current_locationRead the top-frame execution location without stepping. Requires paused debugger.thread_id, process_id
wait_for_pausePoll until any process becomes paused (after a continue_debugger or start_debugging). Returns process info on pause, throws TimeoutException otherwise.timeout_seconds
start_debuggingLaunch an EXE under the dnSpy debugger. By default breaks at EntryPoint (after the module .cctor has run, so ConfuserEx-decrypted bodies are in RAM)exe_patharguments, working_directory, break_kind
attach_to_processAttach the dnSpy debugger to a running .NET process by PIDprocess_id
set_exception_breakpointBreak when a specific exception type is thrown. Configurable first-chance (before catch) and second-chance (unhandled). Default: first-chance enabled.exception_typefirst_chance, second_chance, category
remove_exception_breakpointRemove an exception breakpoint for a specific exception typeexception_typecategory
list_exception_breakpointsList all active exception breakpoints (those with at least one chance flag set)
Parameter details
ParameterTypeDescription
il_offsetintegerIL byte offset within the method body (default 0 = method entry)
conditionstringC# expression evaluated at each breakpoint hit — pauses only when true (e.g. "i > 100", "value != null")
exe_pathstringAbsolute path to the EXE to launch
argumentsstringCommand-line arguments to pass to the process
working_directorystringWorking directory for the launched process
break_kindstringEntryPoint (default, pauses after .cctor) or ModuleCctorOrEntryPoint (pauses before .cctor)
thread_idintegerExplicit thread ID to step/inspect. Defaults to current thread, then first paused thread.
timeout_secondsintegerMax seconds to wait for a step or pause to complete (default 30)
exception_typestringException class name (e.g. System.NullReferenceException, System.IO.IOException)
first_chancebooleanBreak before a catch block handles the exception (default true)
second_chancebooleanBreak on unhandled exceptions (default false)
categorystringException category string (default DotNet). Use DotNet for managed exceptions.

Tip: Use start_debugging + break_kind: EntryPoint for ConfuserEx-packed assemblies — method bodies are decrypted by the time the breakpoint hits. Then use dump_module_from_memory or unpack_from_memory.

Step workflow: break_debugger (or wait for a BP) → get_current_locationstep_over / step_into → inspect with get_local_variables or eval_expression → repeat.


Memory Dump & PE Tools

Extract raw bytes from a debugged process. Requires an active debug session unless otherwise noted.

ToolDescriptionRequired paramsOptional params
list_runtime_modulesEnumerate all .NET modules loaded in the debugged processes with address, size, IsDynamic, IsInMemory, and AppDomainprocess_id, name_filter
dump_module_from_memoryExtract a .NET module from process memory to a file (preserves file layout when possible)module_name, output_pathprocess_id
read_process_memoryRead up to 64 KB from any process address; returns a formatted hex dump and Base64address, sizeprocess_id
write_process_memoryWrite bytes to a process address (hot-patching). Accepts bytes_base64 (base64) or hex_bytes (e.g. "90 90 C3"). Useful for disabling checks without touching the binary on disk.address, (bytes_base64 or hex_bytes)process_id
get_local_variablesRead local variables and parameters from a paused stack frame; returns primitives, strings, and addresses for complex objectsframe_index, process_id
eval_expressionEvaluate a C# expression in the current paused frame context (Watch window equivalent). Returns typed value: primitive, string, or object addressexpressionframe_index, process_id, func_eval_timeout_seconds
get_pe_sectionsList PE section headers of a module in process memory (names, virtual addresses, sizes, characteristics)module_nameprocess_id
dump_pe_sectionExtract a specific PE section (e.g. .text, .data, .rsrc) from a module in process memory; writes to file and/or returns Base64module_name, section_nameoutput_path, process_id
dump_module_unpackedDump a full module with memory-to-file layout conversion (produces a valid loadable PE). Handles .NET, native, and mixed-mode modulesmodule_name, output_pathprocess_id
dump_memory_to_fileSave a contiguous range of process memory to a file. Supports up to 256 MBaddress, size, output_pathprocess_id
Parameter details
ParameterTypeDescription
process_idintegerTarget process ID (use get_debugger_state to find it). Defaults to the first paused process.
name_filterstringGlob or regex filter on module name or filename
module_namestringModule name, full filename, or basename (e.g. MyApp.dll). Use list_runtime_modules to find exact names.
output_pathstringAbsolute path where the dumped bytes will be written. Parent directories are created automatically.
addressstringMemory address in hex (0x7FF000) or decimal
sizeintegerNumber of bytes to read/dump
bytes_base64stringBytes to write as base64 (use with write_process_memory)
hex_bytesstringBytes to write as hex string — "90 90 C3", "9090C3", "0x90 0x90" all accepted
frame_indexintegerStack frame index (0 = top/innermost, default 0)
expressionstringC# expression to evaluate in the current frame context (e.g. "myObj.Field", "arr.Length")
func_eval_timeout_secondsintegerTimeout for function evaluation calls in the debuggee (default 5)
section_namestringPE section name (e.g. .text, .data, .rsrc)

Layout note: dump_module_from_memory reports IsFileLayout in its response. If false, use dump_module_unpacked instead for a corrected PE layout.


Static PE Analysis

Tools that operate on raw PE file bytes — no debug session required.

ToolDescriptionRequired paramsOptional params
scan_pe_stringsScan raw PE file bytes for printable ASCII and UTF-16 strings. Useful for finding URLs, API keys, IP addresses, and embedded plaintext in packed/obfuscated assembliesassembly_namemin_length, encoding
unpack_from_memoryAll-in-one ConfuserEx unpacker: launches the EXE under the debugger (pausing at EntryPoint after decryption), dumps the main module with PE-layout fix, and optionally stops the session. Output can be loaded in dnSpy or passed to deobfuscate_assemblyexe_pathoutput_path, stop_after_dump
Parameter details
ParameterTypeDescription
min_lengthintegerMinimum string length to include (default 4)
encodingstringascii, unicode, or both (default both)
exe_pathstringAbsolute path to the packed EXE to unpack
output_pathstringDestination for the unpacked PE (default: <original_name>_unpacked.exe next to the input)
stop_after_dumpbooleanWhether to stop the debug session after dumping (default true)

Workflow: scan_pe_strings → understand what the packed binary contains → unpack_from_memorydeobfuscate_assembly → load the clean file in dnSpy.


Deobfuscation Tools

Two de4dot integration modes: in-process (deobfuscate_assembly — uses bundled de4dot libraries, available in all builds) and external process (run_de4dot — spawns de4dot.exe, supports dynamic string decryption, available in all builds).

ToolDescriptionRequired paramsOptional params
list_deobfuscatorsList all obfuscator types supported by the in-process de4dot engine
detect_obfuscatorDetect which obfuscator was applied to a .NET assembly file on disk using de4dot's heuristic detectionfile_path
deobfuscate_assemblyDeobfuscate a .NET assembly in-process: renames mangled symbols, deobfuscates control flow, decrypts stringsfile_path, output_pathobfuscator_type, rename_symbols
save_deobfuscatedReturn a previously deobfuscated file as a Base64-encoded blob. Useful when the output file cannot be accessed directlyfile_path
run_de4dotRun de4dot.exe as an external process. Supports dynamic string decryption and ConfuserEx method decryption that require a separate processfile_pathoutput_path, obfuscator_type, dont_rename, no_cflow_deob, string_decrypter, extra_args, de4dot_path, timeout_ms
Parameter details
ParameterTypeDescription
file_pathstringAbsolute path to the .NET assembly on disk
output_pathstringAbsolute path for the cleaned output assembly
obfuscator_typestringForce a specific obfuscator type code (cr for ConfuserEx, un for unknown/auto, etc.). Omit to let de4dot auto-detect.
rename_symbolsboolean(deobfuscate_assembly) Whether to rename obfuscated symbols (default true)
dont_renameboolean(run_de4dot) Skip symbol renaming if true (default false)
no_cflow_deobboolean(run_de4dot) Skip control-flow deobfuscation if true (default false)
string_decrypterstring(run_de4dot) String decrypter mode: none, default, static, delegate, emulate
extra_argsstring(run_de4dot) Additional de4dot command-line arguments passed verbatim
de4dot_pathstring(run_de4dot) Override path to de4dot.exe. Defaults to well-known search paths.
timeout_msinteger(run_de4dot) Max milliseconds to wait for de4dot to finish (default 120000)

Window / Dialog Tools

Enumerate and dismiss dialog boxes (Win32 MessageBox, #32770 dialogs, and WPF windows) that appear in the dnSpy process — for example, error popups that block a debug session. No debug session required.

ToolDescriptionRequired paramsOptional params
list_dialogsList all active dialog/message-box windows. Returns title, HWND (hex), message text, and available button labels for each
close_dialogClose a dialog by clicking a named button. Resolves the target by HWND or picks the first active dialoghwnd, button
Parameter details
ParameterTypeDescription
hwndstringHex HWND of the target dialog, as returned by list_dialogs (e.g. "1A2B3C"). If omitted, the first active dialog is used
buttonstringButton to click (case-insensitive, EN and ES): ok/aceptar, yes/, no, cancel/cancelar, retry/reintentar, ignore/omitir. Default: ok

Button matching — the tool first checks common exact tokens (EN + ES), then falls back to substring matching. If no button matches, WM_CLOSE is sent to the dialog.

Example
// List all open dialogs
{ "tool": "list_dialogs" }

// Dismiss the first dialog by clicking OK
{ "tool": "close_dialog" }

// Dismiss a specific dialog by HWND, clicking Cancel
{ "tool": "close_dialog", "arguments": { "hwnd": "1A2B3C", "button": "cancel" } }

Utility

ToolDescriptionRequired params
list_toolsReturn the full schema for every registered tool as JSON
get_mcp_configReturn the current MCP server configuration and the path to mcp-config.json
reload_mcp_configReload mcp-config.json from disk without restarting dnSpy

Pattern Syntax

Several tools accept a name_pattern or query parameter that supports both glob and regex syntax. The engine auto-detects the mode.

ModeDetected whenExamples
GlobPattern contains only * or ? wildcardsGet*, *Controller, On?Click
RegexPattern contains any of ^ $ [ ( | + {^Get[A-Z], Controller$, ^I[A-Z].*Service$
SubstringNo special characters (for search_types only)Player, Manager

All pattern matching is case-insensitive.

# Find all types whose name starts with "Player"
name_pattern: "Player*"

# Find all interfaces (start with I, followed by uppercase)
name_pattern: "^I[A-Z]"

# Find methods ending in "Async"
name_pattern: "Async$"

# Find all Get* or Set* methods
name_pattern: "^(Get|Set)[A-Z]"

Pagination

List operations return paginated results. The default page size is 50 items.

{
  "items": [ ... ],
  "total_count": 312,
  "returned_count": 50,
  "nextCursor": "eyJvZmZzZXQiOjUwLCJwYWdlU2l6ZSI6NTB9"
}

To fetch the next page, pass the nextCursor value as the cursor argument in the next call. When nextCursor is absent, you have reached the last page.


Usage Examples

Workflow: explore an unknown assembly

1. list_assemblies                           → find "UnityEngine.CoreModule"
2. get_assembly_info  assembly=UnityEngine…  → see namespaces
3. list_types  assembly=…  namespace=UnityEngine  name_pattern="*Manager"
4. get_type_info  assembly=…  type=UnityEngine.NetworkManager
5. decompile_method  …  method=Awake

Search with regex across all assemblies

{ "tool": "search_types", "arguments": { "query": "^I[A-Z].*Repository$" } }

Dump a Unity game module from memory

{ "tool": "list_runtime_modules", "arguments": { "name_filter": "Assembly-CSharp*" } }

{ "tool": "dump_module_from_memory", "arguments": {
    "module_name": "Assembly-CSharp.dll",
    "output_path": "C:\\dump\\Assembly-CSharp_dump.dll"
}}

Set a breakpoint and inspect the call stack

{ "tool": "set_breakpoint", "arguments": {
    "assembly_name": "Assembly-CSharp",
    "type_full_name": "PlayerController",
    "method_name": "TakeDamage"
}}

{ "tool": "get_call_stack" }

Change a private method to public and save

{ "tool": "change_member_visibility", "arguments": {
    "assembly_name": "MyAssembly",
    "type_full_name": "MyNamespace.MyClass",
    "member_kind": "method",
    "member_name": "InternalHelper",
    "new_visibility": "public"
}}

{ "tool": "save_assembly", "arguments": {
    "assembly_name": "MyAssembly",
    "output_path": "C:\\patched\\MyAssembly.dll"
}}

Read process memory at a known address

{ "tool": "read_process_memory", "arguments": {
    "address": "0x7FFE00001000",
    "size": 256
}}

Unpack a ConfuserEx-protected EXE and deobfuscate it

1. scan_pe_strings  assembly_name=MyApp  → confirm it's packed (few readable strings)
2. unpack_from_memory  exe_path=C:\MyApp.exe  output_path=C:\MyApp_unpacked.exe
3. detect_obfuscator  file_path=C:\MyApp_unpacked.exe  → identify remaining obfuscation
4. deobfuscate_assembly  file_path=C:\MyApp_unpacked.exe  output_path=C:\MyApp_clean.dll

Patch anti-debug stubs and save a clean binary

1. list_pinvoke_methods  assembly=MyApp  type=AntiDebugClass
   → finds "CheckRemoteDebuggerPresent" → kernel32.dll
2. patch_method_to_ret  assembly=MyApp  type=AntiDebugClass  method=CheckRemoteDebuggerPresent
3. save_assembly  assembly=MyApp  output_path=C:\MyApp_patched.exe

Load an assembly from disk or from a running process

// Load a .NET DLL from disk
{ "tool": "load_assembly", "arguments": {
    "file_path": "C:\\dump\\MyApp_unpacked.dll"
}}

// Load a raw memory-layout dump (VAs instead of file offsets)
{ "tool": "load_assembly", "arguments": {
    "file_path": "C:\\dump\\MyApp_memdump.bin",
    "memory_layout": true
}}

// Dump from a running process and load directly into dnSpy
{ "tool": "load_assembly", "arguments": {
    "pid": 1234,
    "module_name": "MyPlugin.dll"
}}

Dismiss a dialog that is blocking a debug session

{ "tool": "list_dialogs" }
// → [1] Title: "Error de depuración"
//       Hwnd: 1A2B3C  |  Type: Win32 (#32770)
//       Message: "No se puede continuar la operación."
//       Buttons: Aceptar, Cancelar

{ "tool": "close_dialog", "arguments": { "hwnd": "1A2B3C", "button": "aceptar" } }
// → Clicked 'Aceptar' in dialog 'Error de depuración'.

Find all callers and usages of a suspicious type

{ "tool": "find_who_uses_type", "arguments": {
    "assembly_name": "MyAssembly",
    "type_full_name": "MyNamespace.ObfuscatedLicenseChecker"
}}

{ "tool": "find_who_writes_field", "arguments": {
    "assembly_name": "MyAssembly",
    "type_full_name": "MyNamespace.ObfuscatedLicenseChecker",
    "field_name": "isValid"
}}

Architecture

FileResponsibility
src/Communication/McpServer.csHTTP/SSE listener, JSON-RPC dispatch
src/Application/McpTools.csCentral tool registry, schema definitions, routing
src/Application/AssemblyTools.csAssembly/type listing, P/Invoke analysis
src/Application/TypeTools.csType detail, methods, IL, BFS path analysis
src/Application/EditTools.csMetadata editing, decompilation, assembly saving, method patching
src/Application/DebugTools.csDebugger state, breakpoints, stepping, process launch/attach
src/Application/DumpTools.csRuntime module enumeration, memory dump, PE section tools
src/Application/MemoryInspectTools.csLocal variable inspection from paused debug frame
src/Application/UsageFindingCommandTools.csCross-assembly IL usage analysis (callers, field reads/writes)
src/Application/CodeAnalysisHelpers.csStatic call-graph, dependency chain, dead code analysis
src/Application/De4dotTools.csde4dot in-process integration; available in all builds
src/Application/SkillsTools.csPersistent skills knowledge base (Markdown + JSON) in %APPDATA%\dnSpy\dnSpy.MCPServer\skills\
src/Application/ScriptTools.csRoslyn C# scripting (run_script)
src/Application/WindowTools.csWin32 + WPF dialog enumeration and dismissal
src/Presentation/TheExtension.csMEF entry point, server lifecycle
src/Contracts/McpProtocol.csMCP DTOs (ToolInfo, CallToolResult, …)

Project Structure

dnSpy.MCP.Server/
├── dnSpy.MCP.Server.csproj   # Multi-target: net48 + net10.0-windows
├── CHANGELOG.md
├── README.md
├── RELEASE_NOTES.md
└── src/
    ├── Application/
    │   ├── AssemblyTools.cs         # Assembly & type listing
    │   ├── TypeTools.cs             # Type internals + IL
    │   ├── EditTools.cs             # Metadata editing, method patching
    │   ├── DebugTools.cs            # Debugger integration
    │   ├── DumpTools.cs             # Memory dump & PE tools
    │   ├── MemoryInspectTools.cs    # Local variable inspection
    │   ├── UsageFindingCommandTools.cs  # IL usage analysis
    │   ├── CodeAnalysisHelpers.cs   # Call-graph & dependency analysis
    │   ├── De4dotTools.cs           # de4dot deobfuscation (all builds)
    │   ├── SkillsTools.cs           # Skills knowledge base (MD + JSON)
    │   ├── ScriptTools.cs           # Roslyn C# scripting
    │   ├── WindowTools.cs           # Win32/WPF dialog management
    │   └── McpTools.cs              # Tool registry & routing
    ├── Communication/
    │   └── McpServer.cs             # HTTP/SSE server
    ├── Contracts/
    │   └── McpProtocol.cs           # DTO types
    ├── Helper/
    │   └── McpLogger.cs
    └── Presentation/
        ├── TheExtension.cs          # MEF export / entry point
        ├── ToolbarCommands.cs
        ├── McpSettings.cs
        └── McpSettingsPage.cs

Configuration

mcp-config.json

A mcp-config.json file is created automatically next to the MCP Server DLL on first run. Edit it to change network or de4dot settings — no rebuild required.

{
  "host": "localhost",
  "port": 3100,
  "requireApiKey": false,
  "apiKey": "",
  "enableRunScript": false,
  "de4dotExePath": "",
  "de4dotSearchPaths": [],
  "de4dotMaxSearchDepth": 6
}
FieldDefaultDescription
host"localhost"Bind address. Use "0.0.0.0" to listen on all interfaces (for remote debugging from a sandbox or VM). See note below.
port3100TCP port the server listens on.
requireApiKeyfalseRequire X-API-Key / Authorization: Bearer on every request.
apiKey""API key value. Generate with openssl rand -hex 32.
enableRunScriptfalseEnable the run_script tool (Roslyn C# scripting). Set to true only in trusted environments.
de4dotExePath""Absolute path to de4dot.exe. Leave empty for auto-discovery.
de4dotSearchPaths[]Extra directories to search for de4dot.exe (absolute or relative to this file).
de4dotMaxSearchDepth6Directory levels to walk upward when auto-discovering a sibling de4dot repository.

Remote access — when host is "0.0.0.0" or "*", the HttpListener binds with the wildcard +. This requires a one-time URL ACL reservation (run as Administrator):

netsh http add urlacl url=http://+:3100/ user=Everyone

Then point your MCP client at http://<dnspy-machine-ip>:3100/sse.

After editing mcp-config.json, call reload_mcp_config or restart dnSpy to apply the changes.

Verify the server is running

# Windows (PowerShell)
Invoke-RestMethod http://localhost:3100

# Windows (cmd)
curl http://localhost:3100

# Check port is listening
netstat -ano | findstr :3100

Troubleshooting

SymptomLikely causeSolution
Extension not loadingDLL not in dnSpy bin folderRebuild with -c Release; check output path in .csproj
Connection refused on port 3100Server failed to startCheck dnSpy's log window; port 3100 may be in use — netstat -ano | findstr :3100
Tool returns Unknown tool: …Name typo or outdated client cacheCall list_tools to see the current tool list
Assembly not foundName mismatchCall list_assemblies and use the exact Name value shown
Type not foundWrong type_full_nameUse list_types or search_types to find the exact full name
Debugger is not activeNo debug session runningStart debugging via start_debugging or dnSpy's Debug menu
No paused process foundProcess is still runningCall break_debugger first
dump_module_from_memory returns no bytesModule has no address (pure dynamic)Some in-memory modules emitted by reflection emit cannot be dumped
Dump IsFileLayout: falseMemory layout dumpUse dump_module_unpacked instead — it performs the layout fix automatically
unpack_from_memory fails with anti-debug errorProcess kills itself before EntryPointUse patch_method_to_ret to neutralize anti-debug methods first, save the patched binary, then retry
Failed to reconnect when adding MCP serverWrong transport typeUse --transport sse with Claude Code CLI, not streamable-http. URL must point to /sse endpoint: http://localhost:3100/sse
dump_cordbg_il returns E_NOINTERFACE errorsCOM STA apartment threadingICorDebugModule COM objects belong to the CorDebug engine thread; calling from another STA fails. This is a known limitation — use dump_module_unpacked instead for memory dumps.
Connection refused from VM / sandboxhost is still "localhost"Set "host": "0.0.0.0" in mcp-config.json and run netsh http add urlacl url=http://+:3100/ user=Everyone as Administrator.
Access denied when binding to 0.0.0.0Missing URL ACLRun netsh http add urlacl url=http://+:3100/ user=Everyone as Administrator (replace 3100 with your configured port).
Debug session appears frozen / no responseA dialog box is blocking the UI threadCall list_dialogs to detect open dialogs, then close_dialog to dismiss them and unblock the session.

Contributing

  1. Fork or branch from master
  2. Implement your changes under src/Application/ or src/Communication/
  3. Register new tools in McpTools.cs (GetAvailableTools + ExecuteTool switch)
  4. Build with dotnet build … --nologo — must produce 0 errors, 0 warnings
  5. Manually test via any MCP client (list_tools to verify registration)
  6. Submit a PR with a description of the new tool(s) and their parameters

License

GNU General Public License v3.0 — see for details.