Razor — 18 Operations for AI Agents
Razor pages and Blazor components are where .NET meets the browser, mixing C# and markup in one file. act101 keeps that hybrid structure navigable, so agents find handlers and bindings without losing the seam between code and HTML.
This page is the canonical reference an AI coding agent uses to refactor, query, and analyze Razor code through the act MCP server. 18 operations available: 0 refactor, 18 query, 0 analysis. Each operation is callable from Claude Code, Cursor, Codex, OpenCode, or any MCP-compatible agent host. Click any operation for a stable anchor link suitable for citation.
Worked Razor examples
act101 reads a Razor file's directives — @page's route, @model's type name — as annotation declarations, @inject's declared service as a field, and the C# declarations inside its @code block — classes, properties, methods — under their usual C# kinds. On a real Blazor component that's exactly what act query skeleton returns: two annotation entries for @page/@model, one field for the injected service, a class, two property declarations, and one method — a variable declared directly in @code, outside any class body, produces nothing in the skeleton. symbols covers the same C# declarations plus that stray variable (kind variable, since a bare top-level statement in @code isn't parsed as a class member) and the method's own parameter; the @page and @model directives have no rule in symbols.scm and are absent. The unit of structure this grammar exposes is the individual C# declaration once inside @code; the file's own HTML markup, such as its <h1> element, is read by neither query. Each example below is the verbatim output of the command shown, run against the file shown. Query outputs are pretty-printed with the timing block omitted.
Read the eligibility form's directives and code-behind class
Eligibility.razor is a clinical trial eligibility screening page: @page sets its route, @model names its page model, @inject requests an IEligibilityService, and @code defines an EligibilityCheck class with two properties and an IsEligible method.
$ act query skeleton Eligibility.razor
Before
@page "/trials/eligibility"
@model TrialEligibilityModel
@inject IEligibilityService EligibilityService
<h1>Eligibility Screening</h1>
@code {
public class EligibilityCheck
{
public int MinAge { get; set; }
public bool RequiresBiopsy { get; set; }
public bool IsEligible(Patient patient)
{
return patient.Age >= MinAge;
}
}
private EligibilityCheck screening = new();
}
Output
{
"type": "Skeleton",
"declarations": [
{
"kind": "annotation",
"name": "/trials/eligibility",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 1,
"column": 1,
"byte_offset": 0
},
"end": {
"file": "Eligibility.razor",
"line": 1,
"column": 28,
"byte_offset": 27
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 1,
"column": 8,
"byte_offset": 7
},
"end": {
"file": "Eligibility.razor",
"line": 1,
"column": 27,
"byte_offset": 26
}
}
},
{
"kind": "annotation",
"name": "TrialEligibilityModel",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 2,
"column": 1,
"byte_offset": 28
},
"end": {
"file": "Eligibility.razor",
"line": 2,
"column": 29,
"byte_offset": 56
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 2,
"column": 8,
"byte_offset": 35
},
"end": {
"file": "Eligibility.razor",
"line": 2,
"column": 29,
"byte_offset": 56
}
}
},
{
"kind": "field",
"name": "EligibilityService",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 3,
"column": 1,
"byte_offset": 57
},
"end": {
"file": "Eligibility.razor",
"line": 3,
"column": 47,
"byte_offset": 103
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 3,
"column": 29,
"byte_offset": 85
},
"end": {
"file": "Eligibility.razor",
"line": 3,
"column": 47,
"byte_offset": 103
}
}
},
{
"kind": "class",
"name": "EligibilityCheck",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 8,
"column": 5,
"byte_offset": 149
},
"end": {
"file": "Eligibility.razor",
"line": 17,
"column": 6,
"byte_offset": 390
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 8,
"column": 18,
"byte_offset": 162
},
"end": {
"file": "Eligibility.razor",
"line": 8,
"column": 34,
"byte_offset": 178
}
}
},
{
"kind": "property",
"name": "MinAge",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 10,
"column": 9,
"byte_offset": 193
},
"end": {
"file": "Eligibility.razor",
"line": 10,
"column": 40,
"byte_offset": 224
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 10,
"column": 20,
"byte_offset": 204
},
"end": {
"file": "Eligibility.razor",
"line": 10,
"column": 26,
"byte_offset": 210
}
}
},
{
"kind": "property",
"name": "RequiresBiopsy",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 11,
"column": 9,
"byte_offset": 233
},
"end": {
"file": "Eligibility.razor",
"line": 11,
"column": 49,
"byte_offset": 273
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 11,
"column": 21,
"byte_offset": 245
},
"end": {
"file": "Eligibility.razor",
"line": 11,
"column": 35,
"byte_offset": 259
}
}
},
{
"kind": "method",
"name": "IsEligible",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 13,
"column": 9,
"byte_offset": 283
},
"end": {
"file": "Eligibility.razor",
"line": 16,
"column": 10,
"byte_offset": 384
}
},
"name_range": {
"start": {
"file": "Eligibility.razor",
"line": 13,
"column": 21,
"byte_offset": 295
},
"end": {
"file": "Eligibility.razor",
"line": 13,
"column": 31,
"byte_offset": 305
}
}
}
]
}
The skeleton reports seven declarations: /trials/eligibility and TrialEligibilityModel as annotation, EligibilityService as field, EligibilityCheck as class, MinAge and RequiresBiopsy as property, and IsEligible as method; the screening variable declared after the class, outside any class body, produces nothing.
List the code-behind symbols, including the loose declaration
IsEligible takes a patient parameter, and screening is declared directly in @code after the EligibilityCheck class closes.
$ act query symbols Eligibility.razor
Before
@page "/trials/eligibility"
@model TrialEligibilityModel
@inject IEligibilityService EligibilityService
<h1>Eligibility Screening</h1>
@code {
public class EligibilityCheck
{
public int MinAge { get; set; }
public bool RequiresBiopsy { get; set; }
public bool IsEligible(Patient patient)
{
return patient.Age >= MinAge;
}
}
private EligibilityCheck screening = new();
}
Output
{
"type": "Symbols",
"symbols": [
{
"name": "EligibilityService",
"kind": "field",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 3,
"column": 29,
"byte_offset": 85
},
"end": {
"file": "Eligibility.razor",
"line": 3,
"column": 47,
"byte_offset": 103
}
},
"visibility": "unknown"
},
{
"name": "EligibilityCheck",
"kind": "class",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 8,
"column": 18,
"byte_offset": 162
},
"end": {
"file": "Eligibility.razor",
"line": 8,
"column": 34,
"byte_offset": 178
}
},
"visibility": "unknown"
},
{
"name": "MinAge",
"kind": "field",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 10,
"column": 20,
"byte_offset": 204
},
"end": {
"file": "Eligibility.razor",
"line": 10,
"column": 26,
"byte_offset": 210
}
},
"visibility": "unknown"
},
{
"name": "RequiresBiopsy",
"kind": "field",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 11,
"column": 21,
"byte_offset": 245
},
"end": {
"file": "Eligibility.razor",
"line": 11,
"column": 35,
"byte_offset": 259
}
},
"visibility": "unknown"
},
{
"name": "IsEligible",
"kind": "method",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 13,
"column": 21,
"byte_offset": 295
},
"end": {
"file": "Eligibility.razor",
"line": 13,
"column": 31,
"byte_offset": 305
}
},
"visibility": "unknown"
},
{
"name": "patient",
"kind": "parameter",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 13,
"column": 40,
"byte_offset": 314
},
"end": {
"file": "Eligibility.razor",
"line": 13,
"column": 47,
"byte_offset": 321
}
},
"visibility": "unknown"
},
{
"name": "screening",
"kind": "variable",
"range": {
"start": {
"file": "Eligibility.razor",
"line": 19,
"column": 30,
"byte_offset": 421
},
"end": {
"file": "Eligibility.razor",
"line": 19,
"column": 39,
"byte_offset": 430
}
},
"visibility": "unknown"
}
]
}
symbols reports seven entries covering the same field, class, properties, and method, plus patient as parameter and screening as variable — not field, since nothing outside a class body is parsed as a class member here; the @page and @model directives that appeared in the skeleton have no rule in symbols.scm and are absent.
Query
18 query tools, the same on every supported language. Descriptions live in the shared reference: /docs/query-tools.
callers control_flow data_flow definition diagnostics effect_closure effect_summary fix_auto get_type graph import_organize interface mutations references repo_outline skeleton symbols symbols_batch