Run the Foundgine.SupplyChain sample end to end, then walk through it layer by layer — MCP boundary, application use cases, domain and AOT metadata, semantic model, and PostgreSQL execution. This page follows GUIDE.md in the sample.
What you need.NET 9 SDK, Docker, and the Foundgine repository checked out locally.
What you'll runThe layered Supply Chain MCP sample against a real PostgreSQL database.
What you'll understandWhy each project exists and what it is — and is not — allowed to reference.
What you'll run
The sample is a small supply-chain domain exposed to agents over MCP: customers, orders, order lines, products, suppliers, categories, inventory positions, warehouses, shipments and carriers. It is wired as a stack of single-purpose projects rather than one project with everything in it:
Agent / MCP client
Api
Application
Domain + Semantics
Planning / Execution
Foundgine.Sql
PostgreSQL
Source: samples/Foundgine.SupplyChain in the repository. It is deliberately separate from benchmarks/AgentEndToEnd/SupplyChain, which stays fixed as a benchmark workload while this sample is free to evolve as the recommended reference architecture.
1. Prerequisites
.NET 9 SDK
Docker (for PostgreSQL, and optionally to build the API image)
The sample under samples/Foundgine.SupplyChain references the Foundgine src/ projects directly (rather than the published NuGet packages), so it always builds against the exact source in the repository, including the current AOT source generator. If you copy the sample outside the repository, switch those ProjectReference entries to the published Foundgine.* NuGet packages instead — see PACKAGE-COMPATIBILITY.md in the sample folder.
2. Start PostgreSQL
The sample ships a docker-compose.yml that starts PostgreSQL and the API together:
cd samples/Foundgine.SupplyChain
docker compose up --build
This starts PostgreSQL on localhost:4429 and the API container on localhost:4422, with SupplyChainConnectionString already wired to the containerized database. If you'd rather run the API directly on the host and only containerize the database, start just the postgres service and point SupplyChainConnectionString at it yourself.
3. Run the API
From the repository root, with PostgreSQL reachable and SupplyChainConnectionString set:
dotnet run --project samples/Foundgine.SupplyChain/Api/Foundgine.SupplyChain.Api.csproj
Check that it's up:
curl http://localhost:4422/health
curl http://localhost:4422/health/ready # confirms the database connection
The MCP endpoint is http://localhost:4422/mcp. Point an MCP client at it and you'll see the tools Api/Program.cs exposes — describe_capabilities, get_my_orders, get_order, get_shipment, list_products, list_customers, get_product, get_inventory, list_suppliers, update_inventory, create_shipment, update_shipment, place_order and cancel_order — every one a thin adapter over SupplyChainApplication, never a place that touches SQL directly.
4. Walk the architecture layer by layer
With the sample running, this is the order to read the code in — each layer only talks to the one directly below it.
1→2→3→4→5→6→7→8→9→10
1
Layer 1
API layer — Api
samples/Foundgine.SupplyChain/Api
Api/Program.cs is deliberately small: it creates the ASP.NET host, registers the application/infrastructure composition roots, enables the Foundgine MCP adapter, and maps /mcp and health endpoints. It contains no SQL, business rules, semantic definitions or authorization policy.
Defines the use-case boundary through ISupplyChainQueries and ISupplyChainMutations. SupplyChainApplication performs capability authorization before delegating to the appropriate port.
Swapping MCP for another transport does not require changing the use cases.
3
Layer 3
Domain layer — Domain
samples/Foundgine.SupplyChain/Domain
Contains two intentionally different, unrelated CLR representations of the same business concepts:
Storage records — *ERP types (CustomerERP, SalesOrderERP, CatalogProductERP, InventoryPositionERP...) decorated with FoundgineEntity/FoundgineField/FoundgineRelationship, carrying both semantic and physical names — e.g. SalesOrder stores as table orders, and SalesOrder.Id stores as column order_id.
Application models — Customer, SalesOrder, SalesOrderLine, CatalogProduct, InventoryPosition and friends, named for the business vocabulary rather than the database. The model type does not inherit from or reference the ERP type; the only link is the explicit FoundgineConnection declaration. This keeps the semantic vocabulary stable if the physical schema changes.
4
Layer 4
AOT layer — generated metadata
Foundgine.Aot + Foundgine.Aot.Generator
Foundgine.Aot attributes on the Domain types are compiled by Foundgine.Aot.Generator, which emits Foundgine.Generated.GeneratedMetadata. The sample consumes the generated registry through SupplyChainSemanticModel.Metadata.
The important architectural point: the runtime never rediscovers the storage metadata graph at run time — it's generated at compile time.
5
Layer 5
Semantic layer — Semantics
samples/Foundgine.SupplyChain/Semantics
SupplyChainSemanticModel holds stable semantic IDs for entities and relationships — CatalogProduct, InventoryPosition, SalesOrder, SalesOrderLine — used by semantic operations instead of raw database table names such as products, inventory, orders and order_items. This is the boundary that keeps the sample future-proof against schema changes.
This is the preferred path for any future mutation expressible in Foundgine's mutation IR.
8
Layer 8
High-assurance mutations
place_order / cancel_order
place_order and cancel_order are intentionally different: they carry invariants that are currently PostgreSQL-specific — idempotency/replay protection, advisory transaction locking, FOR UPDATE SKIP LOCKED, inventory reservation races, atomic order + allocation + inventory changes, and cancellation inventory restoration. The sample keeps explicit parameterized SQL here rather than pretending a generic repository abstraction makes those invariants disappear. The long-term direction is to progressively move expressible portions into Foundgine's mutation IR while keeping provider-specific transactional primitives where they're genuinely required.
9
Layer 9
MCP layer
Api/Program.cs — SupplyChainMcpTools
SupplyChainMcpTools contains only protocol adapters. A tool like get_order doesn't know how an order is stored or queried — it invokes the application capability directly. That keeps the MCP surface replaceable and the semantic application architecture transport-independent.
10
Layer 10
Testing layer — Tests
samples/Foundgine.SupplyChain/Tests
The seam for validating each layer independently. Recommended progression: capability authorization tests, AOT metadata tests, semantic plan tests, SQL compilation tests, PostgreSQL integration tests, MCP contract tests, then full agent-facing E2E benchmark tests. The existing benchmarks/AgentEndToEnd/SupplyChain stays untouched and continues to provide the benchmark workload and comparison harness.
The key dependency rule: API → Application → Semantic intent → Foundgine planning → Provider — never API → SQL repository → PostgreSQL directly. That's what lets another MCP transport, another database provider, a different SQL dialect, richer authorization, or additional agent-facing operations get introduced at the right boundary instead of forcing a rewrite of the application.
5. Next steps
Build it from scratch. If you want to understand every project creation step and package reference in order, follow TUTORIAL.md — it starts from an empty solution and ends at this same sample.
Read the full layer-by-layer guide.GUIDE.md is the source for this page, with the complete architecture rationale.
See how a request actually executes. The How it works page follows structured intent through authorization, planning and execution with representative payloads at each step.
Look at the evidence. The Agent Benchmark page includes a dedicated Supply Chain end-to-end report.