EdgeHop

EdgeHop

Stop grepping. Start knowing. EdgeHop turns a codebase into a queryable graph and serves it over MCP — find where a symbol lives, who calls a function, the type hierarchy, and the shortest path between two symbols, resolved from the compiler's semantic model instead of text search. Language support is pluggable: C#/Razor and JavaScript/TypeScript today, with more extractors on the way — and it re

Category
Visit Server

README

<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/.csproj solution, 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.

<!-- Demo GIF: regenerate with vhs Assets/demo.tape (see working/DEMO-GIF.md). --> <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 Save appears in twelve files; they can't tell you which Save a given call actually binds to. EdgeHop uses Roslyn's semantic model, so a CALLS edge is a resolved invocation, not a name collision — the difference between a guess and an answer.
  • Monolithic graph.json extractors (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 HttpClient call 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 JS DotNet.invoke* → the [JSInvokable] C# method it targets.
  • 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-oxc JS/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/.csproj solution, 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-oxc binary 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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured