Architecture
Overview
ProjGraph is a .NET tool ecosystem for visualizing project dependencies, database schemas, and class hierarchies. It exposes two entry points — a CLI and an MCP server — both backed by a shared library layer.
Solution Structure
ProjGraph.slnx
├── src/
│ ├── ProjGraph.Cli # Spectre.Console CLI entry point
│ ├── ProjGraph.Mcp # MCP server entry point (JSON-RPC over stdio)
│ ├── ProjGraph.Lib # Composition root — wires all sub-libraries via DI
│ ├── ProjGraph.Lib.Core # Shared abstractions, parsers, infrastructure
│ ├── ProjGraph.Lib.ProjectGraph # Solution/project dependency graph analysis
│ ├── ProjGraph.Lib.ClassDiagram # C# class hierarchy analysis (Roslyn)
│ ├── ProjGraph.Lib.EntityFramework # EF Core DbContext/ModelSnapshot ERD analysis
│ └── ProjGraph.Core # Shared domain models (SolutionGraph, ClassModel, EfModel)
├── tests/
│ ├── ProjGraph.Tests.Unit.* # Unit tests per library
│ ├── ProjGraph.Tests.Integration.* # Integration tests for CLI and MCP
│ ├── ProjGraph.Tests.Contract # MCP contract & DI wiring tests
│ └── ProjGraph.Tests.Shared # Shared test helpers
└── samples/ # Sample projects used by integration tests
Dependency Graph
Cli ──┐
├──► Lib ──► Lib.Core ──► Core
Mcp ──┘ ├──► Lib.ProjectGraph ──► Lib.Core
├──► Lib.ClassDiagram ──► Lib.Core
└──► Lib.EntityFramework ──► Lib.Core
Key Design Decisions
Composition Root (ProjGraph.Lib)
ProjGraph.Lib is a thin DI composition layer. It exposes a single extension method AddProjGraphLib() that registers
all sub-library services. Both the CLI and MCP server call this method to wire up the full dependency graph.
Abstractions in Lib.Core
Cross-cutting concerns live in Lib.Core.Abstractions:
IFileSystem— File I/O abstraction for testability.IOutputConsole— Console output abstraction; the MCP server substitutes aNullOutputConsoleto prevent ANSI markup on the JSON-RPC transport.ICompilationFactory— Roslyn compilation creation.IDiagramRenderer<T>— Format-agnostic rendering (Mermaid, tree, flat). Each renderer exposes aFormatproperty for keyed resolution.
Use Case Pattern
Each feature library follows a use-case pattern:
Application/
├── IServiceInterface.cs # Public service interface
├── ServiceImplementation.cs # Orchestrates use cases
└── UseCases/
└── SpecificUseCase.cs # Single-responsibility operation
Rendering Pipeline
Diagram generation follows: Parse → Model → Render.
- Parse: Source files are parsed into domain models (
SolutionGraph,ClassModel,EfModel). - Model: Models are pure data (records/classes) in
ProjGraph.Core. - Render:
IDiagramRenderer<T>implementations convert models to output strings. Multiple renderers can be registered for one model type (e.g., tree, flat, and Mermaid forSolutionGraph).
Entity Framework Analysis
EF analysis supports two input types:
- DbContext files — Analyzed via Roslyn semantic analysis of
DbSet<>properties andOnModelCreatingFluent API configurations. - ModelSnapshot files — Analyzed via Roslyn parsing of the compiled migration model.
The Fluent API parser is split across focused classes: FluentApiConfigurationParser (orchestration),
RelationshipConfigParser, PropertyConfigParser, DefaultValueResolver, and FluentApiParsingUtilities.
Class Diagram Analysis
Class analysis uses Roslyn to:
- Parse the target
.csfile or directory (scanning recursively for.csfiles) for type declarations. - For directories, all discovered files are included in a single
CSharpCompilationfor cross-file relationship analysis. - Automatically excludes standard directories:
.git,bin,obj,node_modules. - Optionally discover related types across the workspace (inheritance, dependencies) via
IWorkspaceTypeDiscovery. - Control traversal depth via
maxDepthparameter. - Includes a large-set warning (50+ files) to prevent unreadable diagrams.
Build & Quality
- Target Framework: .NET 10.0
- Central Package Management:
Directory.Packages.props - Code Quality:
TreatWarningsAsErrors=true,EnforceCodeStyleInBuild=true - CI: GitHub Actions on
ubuntu-latestandwindows-latest
Release & Distribution
Releases are triggered by pushing a v* Git tag and are fully automated via .github/workflows/publish.yml.
Release Flow
Tag push (v*)
│
├── Update version in Directory.Build.props & server.json
├── dotnet build + test
├── dotnet pack → ./artifacts/*.nupkg
├── dotnet nuget push → NuGet.org (requires NUGET_API_KEY secret)
├── dotnet nuget push → GitHub Packages (uses GITHUB_TOKEN)
├── mcp-publisher publish (GitHub OIDC auth, no token required)
│ └── Submits src/ProjGraph.Mcp/.mcp/server.json to the Official MCP Registry
│ Retried up to 3× via nick-fields/retry@v3
└── Create GitHub Release with release notes
MCP Registry Ownership Verification
The Official MCP Registry verifies package ownership before accepting a submission by scanning the NuGet package README for a hidden HTML comment:
<!-- mcp-name: io.github.handys11/projgraph -->
This comment must be present at the end of src/ProjGraph.Mcp/README.md. The identifier in the comment must exactly
match the "name" field in src/ProjGraph.Mcp/.mcp/server.json.