Reflection
Register the reflection tool suite to list registered roots, inspect live object state, describe runtime types, and allow tightly scoped writes or method invocations.
Warning: This suite includes Critical tools and can inspect live runtime state, mutate allow-listed members, or invoke allow-listed methods. Keep root registration, traversal boundaries, and write permissions narrowly scoped to local development workflows. You must not ever distribute these tools in a release build.
Install
dotnet add package Ansight.Tools.Reflection
Register the Suite
using Ansight;
using Ansight.Tools.Reflection;
var session = new DebugSessionViewModel();
var options = Options.CreateBuilder()
.WithReflectionTools(reflection =>
{
reflection.WithDefaultMemberVisibility(ReflectionMemberVisibility.PublicOnly);
reflection.WithAssemblyTraversalMode(ReflectionAssemblyTraversalMode.AllowListedOnly);
reflection.WithNamespaceTraversalMode(ReflectionNamespaceTraversalMode.AllowListedOnly);
reflection.AllowNamespacePrefix("MyApp.ViewModels");
reflection.AddRoot(
"session",
session,
new ReflectionRootMetadata("Current Session")
{
Description = "Active session view model",
Category = "view-model",
Tags = ["debug", "session"],
ContainsSensitiveData = true,
Attributes = new Dictionary<string, string>
{
["team"] = "sdk"
}
},
root => root
.AllowWritableMembers("SelectedTab")
.AllowAllWritableMembersOn<DebugSessionViewModel>()
.AllowInvokableMethods("Refresh()", "Child#Rename(System.String)")
.AllowAllInvokableMethodsOn<DebugSessionViewModel>());
})
.WithReadWriteToolAccess()
.Build();
Registration API
AddRoot(string id, object target, ReflectionRootMetadata metadata, ...): registers a direct weak reference. This is the default for direct object registration.AddStrongRoot(...): registers a direct strong reference when the tool suite should retain the object.AddRoot(string id, Func<object?> resolver, ReflectionRootMetadata metadata, ...): resolves the object on demand for each tool call.WithAssemblyTraversalMode(...): chooseAllowListedOnlyorAllowAllfor assembly-based traversal boundaries.WithNamespaceTraversalMode(...): chooseAllowListedOnlyorAllowAllfor namespace-based traversal boundaries.AllowAssembly(...)/AllowAssemblies(...): add assembly allow-list entries when assembly traversal stays inAllowListedOnly.AllowNamespacePrefix(...)/AllowNamespacePrefixes(...): add namespace-prefix allow-list entries when namespace traversal stays inAllowListedOnly.WithDefaultMemberVisibility(...): set the global visibility rule. The default isPublicOnly.WithMemberVisibility(...): override member visibility for one root.AllowWritableMembers(...): allow field or property writes by relative path such asSelectedTaborChild.Name.AllowAllWritableMembersOn<T>()/AllowAllWritableMembersOn(params Type[] types): allow writes for all writable fields and properties on reachable objects assignable to those types.AllowAllWritableMembers(): allow writes for all writable fields and properties reachable from the root.AllowInvokableMethods(...): allow instance method calls usingMethod(Type)for root targets orPath#Method(Type)for nested targets.AllowAllInvokableMethodsOn<T>()/AllowAllInvokableMethodsOn(params Type[] types): allow all instance methods on reachable objects assignable to those types.AllowAllInvokableMethods(): allow all instance methods reachable from the root.
Root Metadata
ReflectionRootMetadata requires DisplayName and also supports:
DescriptionCategoryTagsContainsSensitiveDataAttributes
Specific Concerns
- The suite never reflects arbitrary process objects. Apps must register every root explicitly.
- Direct object roots use weak references by default. Use
AddStrongRoot(...)only when the root must stay alive for the lifetime of the suite. - Expansion is stateless. Every call resolves from the root again rather than using persistent object handles.
- Recursive traversal is allow-listed by default. If you do not configure allow-list entries or explicitly switch a traversal mode to
AllowAll, child object snapshots stay opaque. - Types outside the configured assembly or namespace boundary return opaque previews instead of recursive members.
- Non-public members stay hidden unless you opt into
ReflectionMemberVisibility.PublicAndNonPublic. - Reads are controlled by root registration, traversal boundaries, and member visibility. You do not need a separate read allow-list.
reflect.set_member_valueandreflect.invoke_methodareWritetools. They require explicit per-root path, type, or wildcard enablement even when the runtime guard allows writes.- Collection-element writes and static method invocation are out of scope in v1.
If you want unrestricted traversal within registered roots, opt in explicitly:
reflection.WithAssemblyTraversalMode(ReflectionAssemblyTraversalMode.AllowAll);
reflection.WithNamespaceTraversalMode(ReflectionNamespaceTraversalMode.AllowAll);
Path Syntax
Property.SubProperty[index]["key"]
Tool Matrix
| Name | Id | Scope | Description | Security |
|---|---|---|---|---|
List Reflection Roots | reflect.list_roots | Read | Lists the registered live object roots and their capabilities. | High |
Inspect Object | reflect.inspect_object | Read | Returns a stateless snapshot of a registered live object or nested path. | Critical |
Describe Type | reflect.describe_type | Read | Returns runtime type metadata without reading live object values. | Moderate |
Set Member Value | reflect.set_member_value | Write | Writes an explicitly allow-listed field or property. | Critical |
Invoke Method | reflect.invoke_method | Write | Invokes an explicitly allow-listed instance method. | Critical |
List Reflection Roots
Arguments:
- none
Returns:
rootscountcapturedAtUtc
Each root descriptor includes:
idmetadataregistrationKindreferenceStrengthavailableruntimeTypememberVisibilitycanWriteMemberscanInvokeMethodsresolutionError
Example:
{
"toolId": "reflect.list_roots",
"arguments": {}
}
Inspect Object
Arguments:
root: required registered root idpath: optional nested member or collection pathmaxDepth: recursive expansion depth, default1, maximum4maxItemsPerCollection: collection item limit, default10, maximum64
Returns:
rootpathsnapshotcapturedAtUtc
Snapshot payloads include common fields such as:
declaredTyperuntimeTypekindisNullpreviewexpandable
Expanded snapshots may also include:
membersmethodsitemstruncatedopaquecycleDetected
Example:
{
"toolId": "reflect.inspect_object",
"arguments": {
"root": "session",
"path": "Items[1]",
"maxDepth": 2,
"maxItemsPerCollection": 10
}
}
Describe Type
Arguments:
typeName: required runtime type nameassemblyName: optional assembly name
Returns:
typeNameassemblyNamenamespacekindbaseTypeinterfacesgenericAritymemberVisibilitymembersmethodscapturedAtUtc
Example:
{
"toolId": "reflect.describe_type",
"arguments": {
"typeName": "MyApp.DebugSessionViewModel",
"assemblyName": "MyApp"
}
}
Set Member Value
Arguments:
root: required registered root idpath: required allow-listed member pathvalueJson: required JSON-encoded replacement value
Returns:
rootpathupdatedsnapshotcapturedAtUtc
The write path must end on a settable field or property. Collection-element writes such as Items[0] are not supported.
Example:
{
"toolId": "reflect.set_member_value",
"arguments": {
"root": "session",
"path": "SelectedTab",
"valueJson": "\"details\""
}
}
Invoke Method
Arguments:
root: required registered root idtargetPath: optional nested target pathmethod: required method nameparameterTypesJson: optional JSON array of parameter type names for overload disambiguationargumentsJson: optional JSON array of argument values
Returns:
roottargetPathsignatureinvokedreturnSnapshotcapturedAtUtc
Overloaded methods require parameterTypesJson. Nested targets use the same path grammar as reflect.inspect_object.
Example:
{
"toolId": "reflect.invoke_method",
"arguments": {
"root": "session",
"targetPath": "Child",
"method": "Rename",
"parameterTypesJson": "[\"System.String\"]",
"argumentsJson": "[\"Updated Child\"]"
}
}