CodeReach.Mcp 0.2.0

{
  "servers": {
    "CodeReach.Mcp": {
      "type": "stdio",
      "command": "dnx",
      "args": ["CodeReach.Mcp@0.2.0", "--yes"]
    }
  }
}
                    
This package contains an MCP Server. The server can be used in VS Code by copying the generated JSON to your VS Code workspace's .vscode/mcp.json settings file.
dotnet tool install --global CodeReach.Mcp --version 0.2.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local CodeReach.Mcp --version 0.2.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=CodeReach.Mcp&version=0.2.0
                    
nuke :add-package CodeReach.Mcp --version 0.2.0
                    

CodeReach

Which endpoints and jobs reach this code, and what does your diff affect.

CodeReach is an MCP server for ASP.NET Core and C# solutions. It loads your solution with Roslyn, builds a call graph from the compiler's own semantic model, keeps it on disk, and answers questions an agent (or you, from a shell) would otherwise answer with grep and guesswork:

  • What calls this method, through its interface, all the way up to a controller action, a minimal API handler or a background service?
  • Which endpoints exist, with their full route and authorization, including your own permission attributes?
  • I changed these lines. Which endpoints and jobs does that touch?

Status: 0.1.0, not yet published to NuGet. The commands below that use dnx work once it is.

Why not grep, or a tree-sitter index

Text and syntax tools cannot see what the compiler sees. In a layered application (controller, service interface, service, repository interface, repository) they stop at the first interface, count comments and strings, attach calls to the wrong overload, and report a shared generic base method as every repository's hotspot. CodeReach binds every name with Roslyn, so:

  • a call through IOrderService reaches OrderService, and says so;
  • PermissionKeys.Orders.Delete and PermissionKeys.Products.Delete are different symbols;
  • OrderRepository.GetByIdAsync counts only calls an OrderRepository can receive, even though the method lives in Repository<T>;
  • tests are excluded because their project references a test framework, not because of a name;
  • every tool reads one graph, so a caller count, a usage count and a trace never disagree.

Run it

Requires the .NET 10 SDK, and a solution that builds.

{
  "mcpServers": {
    "codereach": {
      "command": "dnx",
      "args": ["CodeReach.Mcp", "--yes"]
    }
  }
}

The server indexes the single .slnx, .sln or .csproj in its working directory. To point it elsewhere, append "--", "--solution", "path/to/App.slnx" to args.

From a clone of this repository:

dotnet run --project src/CodeReach.Mcp -- call trace_to_entrypoints '{"symbol":"OrderRepository.HardDeleteAsync"}' --solution samples/SampleShop
entry points that reach OrderRepository.HardDeleteAsync(Guid)
4 entry points (3 endpoint, 1 hosted-service), 1 unrooted tops; 10 symbols visited. Hidden: 2 call sites in test projects (include_tests=true shows them).

[endpoint] DELETE /api/Orders/{id:guid}   auth: HasPermission(PermissionKeys.Orders.Delete = "orders.delete") [action]; Authorize [base ShopControllerBase]
  OrdersController.Delete(Guid)  src/Shop.Api/Controllers/OrdersController.cs:17
  -> OrderPurger.PurgeAsync(Guid)  called at src/Shop.Api/Controllers/OrdersController.cs:19  (bound to IOrderPurger.PurgeAsync(Guid))
  -> OrderRepository.HardDeleteAsync(Guid)  called at src/Shop.Application/Orders/OrderService.cs:47  (bound to IOrderRepository.HardDeleteAsync(Guid))

[hosted-service] OrderPurgeService
  OrderPurgeService.ExecuteAsync(CancellationToken)  src/Shop.Infrastructure/Jobs/OrderPurgeService.cs:9
  -> OrderPurgeService.PurgeExpiredAsync()  called at src/Shop.Infrastructure/Jobs/OrderPurgeService.cs:13
  ...

A local page, if you prefer looking to asking

codereach ui --solution path/to/App.slnx

opens a page in your browser with a command bar (type a method, a route, impact or di) and a health report: endpoints without authorization, captive dependencies, unregistered services, and what your uncommitted changes reach. It is served by the tool itself on 127.0.0.1 with a one-time token, answers from the same tools as above, and stops with Ctrl+C. Nothing is hosted anywhere.

CodeReach is a plain MCP server that runs entirely on your machine: no account, no hosted service, nothing sent anywhere. It installs no hooks, skills or agent profiles, never edits your client configuration, and writes nothing into the solution it analyses. Its index lives in your user cache directory.

Tools

Tool Answers
trace_to_entrypoints Every endpoint, hosted service and other root that reaches a symbol, with one call path each
impact A git diff (uncommitted changes, a base ref, or diff text) mapped to the endpoints and jobs it affects
routes Endpoints with verb, full route template, handler and authorization; auth=none lists the unprotected ones
callers / callees The call relation with interface and virtual dispatch resolved, to a chosen depth
find_usages Every reference bound to a symbol, by kind (call, read, write, nameof, attribute); works for framework members
find_symbol Declarations by name
entrypoints All roots a trace can stop at
architecture Projects, how much each really uses the others, most-called methods, most-implemented interfaces
type_hierarchy What a type inherits and implements, every type deriving from it (direct or indirect), its members and what each implements or overrides
di_registrations Every AddScoped/AddSingleton/AddTransient/TryAdd/keyed/hosted/AddHttpClient/AddDbContext registration and what each constructor asks for; reports captive dependencies, injected but unregistered services, and double registrations
status / reindex Index state and freshness; force a refresh

Prompts

Ready-made requests that tell the agent which tools to call, in order. Every MCP client shows them; in Claude Code they appear as /mcp__codereach__<name>.

Prompt Does
review-diff What your change affects: endpoints with auth, jobs, implementations that must change too, DI problems
before-change The blast radius of changing one symbol, and which tests cover it
add-feature Finds how similar features are built here and plans the files to add, following the same layers
audit-auth Endpoints with no authorization or anonymous access, ranked by risk
explain-endpoint One endpoint from route to data access, through every layer

A symbol is named as Type.Member, as path/File.cs:line, or with an id from an earlier answer. An ambiguous name is refused with the candidates listed. Every tool is also a shell command: codereach call <tool> '{json}', exiting 0 when answered, 1 on a finding, 2 when it could not run.

What every answer tells you

  • How much the default filters hid. "Hidden: 7 call sites in test projects."
  • Whether the analysis was complete. The last line is analysis: complete, or PARTIAL with the projects or documents that failed to load or bind. A zero is only printed for a complete analysis.
  • Whether the index is current. A first line starting with STALE means files changed and the refresh is still running.
  • What it could not see. A call chain that ends where nothing calls it is reported as unrooted (dependency injection, reflection, or dead code), not silently dropped.

Staying fresh

The first index of a large solution takes about as long as a build. After that the index is a file: a server that starts on unchanged sources answers in milliseconds and never loads Roslyn. When files change, a body-only edit re-extracts that one document; a change to declarations re-extracts the project and the projects that depend on it. Freshness is checked on every call and does not rely on file watching.

Configuration

Optional codereach.json next to the solution:

{
  // Attributes that mean authorization but implement no framework contract.
  "authAttributes": ["HasPermission"],
  // Fluent endpoint methods that mean authorization, besides RequireAuthorization.
  "authEndpointMethods": ["RequirePermission"],
  // Source that should not be indexed.
  "exclude": ["**/Migrations/**"],
  // Extra entry points: a method that implements an interface member, derives from a base type, or carries an attribute.
  "entryPoints": [
    { "kind": "command-handler", "implements": "MyApp.ICommandHandler`1", "method": "HandleAsync" }
  ]
}

Attributes that implement the framework's own authorization contracts are recognised without configuration, and so are MediatR handlers, MassTransit consumers, Quartz jobs, middleware, SignalR hubs and Azure Functions.

What it does not model

  • Implicit user-defined conversions, and the calls behind foreach, using, await, deconstruction and collection initializers.
  • Reflection, and constructors invoked by a dependency injection container (registrations themselves are read by di_registrations, except those made by assembly scanning or inside a library).
  • Calls through an interface with several implementations are reported for every implementation the receiver's static type allows, and each hop says how many candidates there were.
  • Global authorization (a fallback policy, a global authorize filter) is detected and reported next to the route list, not applied to individual endpoints.
  • Conventional (non-attribute) MVC routes are listed as (conventional).
  • C# only. Razor Pages, gRPC and SignalR are reachable through entry-point rules but are not listed as routes.

Development

dotnet build
dotnet test --project tests/CodeReach.Tests

The tests index samples/SampleShop, a small fictional layered application that contains every trap listed above, and compare the result with Roslyn's own reference finder, with complete hand-audited route tables, and with a from-scratch index after each kind of incremental edit. Design and plan are in docs/ARCHITECTURE.md and docs/PLAN.md.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.2.0 0 11/10/2026
0.1.0 0 11/10/2026