EdgeHop 0.1.23-alpha
dotnet tool install --global EdgeHop --version 0.1.23-alpha
dotnet new tool-manifest
dotnet tool install --local EdgeHop --version 0.1.23-alpha
#tool dotnet:?package=EdgeHop&version=0.1.23-alpha&prerelease
nuke :add-package EdgeHop --version 0.1.23-alpha
<p align="center"> <img src="https://raw.githubusercontent.com/EdgeHop/EdgeHop/main/Assets/Images/edgehop-title.svg" alt="EdgeHop" width="100%"> </p>
<h3 align="center">Stop grepping. Start knowing.</h3>
<p align="center"> <a href="https://github.com/EdgeHop/EdgeHop/actions/workflows/ci.yml"><img src="https://github.com/EdgeHop/EdgeHop/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://www.nuget.org/packages/EdgeHop"><img src="https://img.shields.io/nuget/vpre/EdgeHop.svg" alt="NuGet"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache 2.0"></a> </p>
EdgeHop builds an accurate, durable graph of the symbols in a solution — and the relationships between them — then serves it to an AI assistant over the Model Context Protocol (MCP). It doesn't match text; it understands code. Every relationship is resolved from the compiler's own semantic model, so a call, an implementation, or a reference is the actual one the compiler binds — not a name that happens to look alike. Instead of guessing at call chains from text search, your assistant asks "who calls this method?", "what implements this interface?", or "what breaks if I change this?" and gets answers grounded in that semantic understanding — including across tier boundaries the compiler can't cross: the Web-to-API HTTP call and the C#-to-JavaScript interop bridge.
Project types. EdgeHop is built .NET-first, but it is not limited to .NET. Any directory works as a target: a
.sln/.csprojsolution, a mixed C#+JS/TS Blazor app, or apure HTML/JavaScript/TypeScriptproject with no .NET at all. Additional languages and project types are added through extractor plugins.
<p align="center"> <img src="https://raw.githubusercontent.com/EdgeHop/EdgeHop/main/Assets/Images/demo.gif" alt="EdgeHop tracing a Blazor UI handler across the HTTP boundary to its API endpoint" width="100%"> </p>
What it does
- Indexes a
.sln— or any project directory — into a code graph: C# symbols from Roslyn's semantic model, and JavaScript/TypeScript symbols from a native oxc parse — merged into one graph per git branch. .NET is the primary focus, but the target need not be a .NET project at all: point it at a pure HTML/JS/TS tree (no.sln, no Visual Studio, no .NET involved) and the oxc extractor graphs it while the Roslyn extractor simply no-ops. - Captures real relationships, not text matches: calls, interface implementations, inheritance, overrides, type references, containment, Blazor component rendering, and two cross-tier bridges — HTTP client-to-endpoint calls and bidirectional C#↔JS interop.
- Serves the graph over MCP so an assistant (e.g. Claude Code) can traverse it with five focused tools, and over a plain CLI for scripting.
- Stays current with your working tree via watch mode and optional git hooks that re-index in the background on commit, merge, and checkout — per branch, without touching your working directory.
Why it's different
Most "code intelligence for AI" tooling falls into one of two camps, and EdgeHop deliberately avoids both:
- Text / Tree-sitter indexers parse syntax without resolving symbols. They can tell you
a token named
Saveappears in twelve files; they can't tell you whichSavea given call actually binds to. EdgeHop uses Roslyn's semantic model, so aCALLSedge is a resolved invocation, not a name collision — the difference between a guess and an answer. - Monolithic
graph.jsonextractors (e.g. Graphify-style tools) dump the whole graph to a single file that must be regenerated wholesale on every change. EdgeHop stores the graph in an indexed, durable, incrementally-reconciled database and updates only what changed, so it scales and stays truthful as you work.
Beyond that, three things are unusual:
- Cross-tier edges. Call chains stay walkable straight across boundaries a compiler
can't cross:
- Web tier → API. A Web-tier
HttpClientcall is linked to the C# method that serves the matching endpoint (by verb + route template). - C# ↔ JavaScript interop, both directions.
IJSRuntime.InvokeAsync→ the JS export it invokes, and JSDotNet.invoke*→ the[JSInvokable]C# method it targets.
- Web tier → API. A Web-tier
- Branch-aware and local-first. The graph is scoped per git branch and lives entirely on your machine. No shared server, no credentials, no telemetry. Switch branches and the next query reflects it.
- Pluggable storage and extractors. SQLite by default (embedded, zero-config), Neo4j optional. The C# and JS/TS extractors are independent, reflection-loaded plugins — the same seam is how support for other languages and project types will be added over time, without changing the core or the query surface.
Requirements
- .NET 10 SDK/runtime
- win-x64, linux-x64, or osx-arm64 — the only platform-specific dependency is the
bundled
edgehop-oxcJS/TS parser, a native binary shipped for those three platforms. Everything else is portable .NET. See Roadmap — a platform-independent oxc parser is planned, which will drop the native-binary requirement entirely. - No database or credentials for the default SQLite backend. Neo4j is opt-in.
Project types. EdgeHop is built .NET-first, but it is not limited to .NET. Any directory works as a target: a
.sln/.csprojsolution, a mixed C#+JS/TS Blazor app, or a pure HTML/JavaScript/TypeScript project with no .NET at all. Additional languages and project types are added through extractor plugins.
Installation
EdgeHop ships as a single .NET global tool:
dotnet tool install -g EdgeHop --prerelease
dotnet tool update -g EdgeHop --prerelease # later, to upgrade
Quick start
1. Index a solution (or a directory):
edgehop index C:\path\to\YourApp.sln
2. Query it from the CLI:
edgehop find-symbol OrderService
edgehop get-callers <symbolId> --depth 3
edgehop get-relationships <symbolId> --edge-type IMPLEMENTS
edgehop get-path <fromId> <toId>
edgehop stats
Add --json to any query verb for machine-readable output.
3. Wire it into your AI assistant — point an MCP client at the edgehop mcp command.
For Claude Code, add to .mcp.json:
{
"mcpServers": {
"edgehop": {
"command": "edgehop",
"args": ["mcp"],
"env": { "EDGEHOP_REPO": "C:\\path\\to\\your\\solution" }
}
}
}
4. Keep it fresh — watch mode, or install background git hooks:
edgehop index C:\path\to\YourApp.sln --watch
edgehop install-hooks C:\path\to\YourApp.sln # re-index on commit / merge / checkout
What the graph captures
Node kinds: namespaces, types (class/struct/interface/enum), methods, properties, fields, Blazor components, and JS/TS functions.
Edge types:
| type | meaning |
|---|---|
CONTAINS |
namespace → type, type → member |
CALLS |
method invocation (resolved via the semantic model) |
IMPLEMENTS |
class/struct implements interface |
INHERITS |
derived type → base type |
OVERRIDES |
override → overridden method |
REFERENCES |
a symbol uses a type (parameter, return, field type, …) |
RENDERS |
a Blazor component renders a child component in its markup |
HTTP_CALLS |
a Web-tier HttpClient call → the C# method serving that endpoint |
JS_CALLS |
C# IJSRuntime interop call → the JS function it invokes |
JS_INVOKES |
JS DotNet.invoke* → the [JSInvokable] C# method it targets |
The MCP query surface
| tool | answers |
|---|---|
find_symbol |
where a symbol lives; whether it's a component; which route it serves |
get_callers |
who calls a method, N hops deep — across HTTP and JS interop too |
get_relationships |
everything related to a symbol by any edge type; filter by direction/type |
get_path |
the shortest directed path between two symbols (reachability/impact) |
graph_stats |
per-branch totals, counts by kind and edge type, and the busiest nodes |
Text and content search stays your assistant's grep job — EdgeHop indexes structure, not
text.
Configuration
All configuration is environment variables (no config file, no stored credentials):
| variable | purpose |
|---|---|
EDGEHOP_BACKEND |
sqlite (default) or neo4j |
EDGEHOP_SQLITE_PATH |
override the store file (default is derived per repo under %LOCALAPPDATA%\EdgeHop\stores\) |
EDGEHOP_REPO |
which repo's current branch the MCP server follows |
EDGEHOP_BRANCH |
force a branch (otherwise resolved from git) |
EDGEHOP_EXTRACTORS |
subset the loaded extractors (default: all) |
EDGEHOP_JS_INTEROP |
C#↔JS interop match mode: precise (default) / broad / off |
NEO4J_URI etc. |
Neo4j connection info, read only when EDGEHOP_BACKEND=neo4j |
The SQLite store is per-solution and derived from the repo, so multiple solutions index side by side with zero configuration.
How it works
C#/Razor ──▶ Roslyn extractor ─┐
├─▶ reconcile (per-branch diff) ─▶ graph store ─▶ MCP / CLI
JS/TS ─────▶ oxc extractor ────┘ (SQLite/Neo4j)
Extraction is whole-solution; storage is incremental — each index run reconciles the new graph against the stored one for that branch and applies only the difference. Stores and extractors are reflection-loaded plugins, so neither the core nor the host depends on a specific database driver or on MSBuild.
A worked example
The commands below run against the in-repo sample Blazor Server app at
tests/samples/EdgeHopExplorer.BlazorServer — a small app whose Blazor UI calls a typed
HttpClient that hits a minimal-API endpoint, so it exercises the cross-tier edges. First
index it:
$ edgehop index tests/samples/EdgeHopExplorer.BlazorServer/EdgeHopExplorer.BlazorServer.sln
Extraction complete: 115 nodes, 168 edges.
oxc: 2 module(s) 11 nodes, 12 edges.
JS interop (precise): 2 C# call site(s), 3 JS export(s), 2 JS_CALLS edge(s).
DotNet interop (precise): 2 JS call site(s), 2 [JSInvokable] method(s), 2 JS_INVOKES edge(s).
1. Find a symbol — locate the Web-tier client method and its stable id:
$ edgehop find-symbol GetAllAsync
Method Task<IReadOnlyList<FeatureInfo>> FeatureApiClient.GetAllAsync()
id: Method:System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<EdgeHopExplorer.BlazorServer.Domain.FeatureInfo>> EdgeHopExplorer.BlazorServer.Services.FeatureApiClient.GetAllAsync()
doc: Services/FeatureApiClient.cs
1 match on branch 'main'.
2. Look up its relationships — what does this method reach? Note the HTTP_CALLS edge:
EdgeHop resolved this client call to the minimal-API method that serves the matching route,
across a tier boundary the compiler can't cross:
$ edgehop get-relationships "Method:...FeatureApiClient.GetAllAsync()" --direction out
HTTP_CALLS out
Method IEndpointRouteBuilder FeatureEndpoints.MapFeatureEndpoints(IEndpointRouteBuilder app)
id: Method:...EdgeHopExplorer.BlazorServer.Endpoints.FeatureEndpoints.MapFeatureEndpoints(...)
doc: Endpoints/FeatureEndpoints.cs
routes: GET /api/features/all, GET /api/features/{name}, POST /api/features/search
REFERENCES out
NamedType FeatureInfo
id: NamedType:EdgeHopExplorer.BlazorServer.Domain.FeatureInfo
doc: Domain/FeatureInfo.cs
2 relationships on branch 'main'.
3. Find a path from one call to another — trace how the Blazor page's init handler
reaches the API endpoint. The shortest directed path walks a CALLS edge and then the
cross-tier HTTP_CALLS edge in a single traversal:
$ edgehop get-path "Method:...Features.OnInitializedAsync()" "Method:...FeatureEndpoints.MapFeatureEndpoints(...)"
Path ... (2 hops):
Task Features.OnInitializedAsync() --CALLS--> Task<IReadOnlyList<FeatureInfo>> FeatureApiClient.GetAllAsync() --HTTP_CALLS--> IEndpointRouteBuilder FeatureEndpoints.MapFeatureEndpoints(IEndpointRouteBuilder app)
That's a Blazor UI handler → typed HTTP client → API endpoint chain, reconstructed from the graph rather than guessed from text.
4. See which routes that endpoint serves — the path lands on the minimal-API method; inspect it and EdgeHop reports the concrete, verb-prefixed HTTP routes it registers, so the trace ends at the actual URLs the UI ultimately reaches:
$ edgehop find-symbol MapFeatureEndpoints
Method IEndpointRouteBuilder FeatureEndpoints.MapFeatureEndpoints(IEndpointRouteBuilder app)
id: Method:...EdgeHopExplorer.BlazorServer.Endpoints.FeatureEndpoints.MapFeatureEndpoints(...)
doc: Endpoints/FeatureEndpoints.cs
routes: GET /api/features/all, GET /api/features/{name}, POST /api/features/search
1 match on branch 'main'.
Add --json to any query verb for the exact MCP tool shape.
Roadmap
- Platform-independent JS/TS parser. EdgeHop ships the native
edgehop-oxcbinary for win-x64, linux-x64, and osx-arm64 today. A cross-platform oxc parser is planned, which will drop the native-binary requirement entirely and let EdgeHop run anywhere .NET 10 does. - More extractor plugins. The reflection-loaded extractor seam is designed to grow beyond C# and JS/TS to additional languages and project types.
Contributing
Contributions are welcome — see CONTRIBUTING.md for how to build, test, and submit changes.
License
Licensed under the Apache License 2.0.
Built on oxc (JS/TS parsing) and Roslyn (C#/Razor analysis). Bundled third-party components and their licenses are listed in THIRD-PARTY-NOTICES.md.
| Product | Versions 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. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.23-alpha | 1,467 | 7/21/2026 |
| 0.1.5-alpha | 72 | 7/20/2026 |