Overview
NexaFlowDemo is a complete, runnable Visual Studio 2022 demonstration of AI Agents, Real Model Context Protocol (MCP), Retrieval-Augmented Generation (RAG), Parallel Agent Execution, Event-Driven Workflows, Multi-Tenant Support, Audit Logging, and Carrier/ERP Integration — all wired into a realistic supply chain application. The fictional company NexaFlow Logistics operates 6 SKUs, 6 suppliers, and live shipment tracking — powered by real Claude AI and real MCP protocol over JSON-RPC 2.0 / stdio.
This is the MCP Version: MCP tools are hosted in a separate NexaFlowDemo.McpServer console app and invoked via the official ModelContextProtocol v1.3.0 NuGet package. The web app spawns the McpServer as a subprocess and communicates over real JSON-RPC 2.0 stdio transport through RealMcpClientService.
Architecture
AgentModels, Document, McpModels (McpInvokeResponse, McpToolDefinition)ILlmService → ClaudeService (live Anthropic API; claude-sonnet-4-6)IEmbeddingService → SimulatedDenseEmbeddingService — 384-dim FNV-1a hash vectorsIVectorStore → InMemoryVectorStore + SqliteVectorStore; cosine similarity searchInMemoryDocumentStore (TF-IDF) + VectorDocumentStore; RagPipeline + RagEvaluatorNexaFlowDemo.McpServer); 9 tool classes with [McpServerToolType] / [McpServerTool] attributes; WithToolsFromAssembly auto-registration; real JSON-RPC 2.0 stdio serverRealMcpClientService → McpClient.CreateAsync + StdioClientTransport; spawns McpServer subprocess; SemaphoreSlim lazy-init; routes all tool calls over JSON-RPC 2.0ReactAgent (ReAct loop) • ParallelReactAgent (concurrent tool calls) • PlanningAgent (plan→execute) • MultiAgentOrchestratorMemoryConsolidator (Jaccard merging); SQLite conversation persistenceWorkflowEngine + ReorderWorkflow (inventory_low) + SlaBreachWorkflow (sla_breach); event-driven trigger systemITenantContext + TenantRegistry (3 demo tenants: Acme, Globex, Initech); TenantScopedDocumentStoreIAuditLogger / InMemoryAuditLogger; ICarrierApiService (SimulationMode); ErpTools (SAP-compatible)Feature Pages
💬 RAG Chat (/Chat)
Ask natural-language questions about NexaFlow's inventory policies, supplier contracts, SLAs, and AI forecasting system. Powered by TF-IDF + dense vector search over 6 pre-seeded documents.
🧠 ReAct Agent (/Agent)
The agent thinks, calls MCP tools via real JSON-RPC over stdio, observes results, and loops until it has a final answer. Watch the Think → Act → Observe cycle live with inventory lookups, supplier queries, and shipment tracking.
🆕 Multi-Agent Pipeline (/Orchestration)
Two agents collaborate via SignalR streaming: a Research Agent gathers supply chain facts, then a Summary Agent synthesizes a final executive response. Real-time step-by-step rendering.
🔧 MCP Tools (/Mcp)
Browse all 9 MCP tools hosted in the McpServer subprocess, click to select, edit JSON parameters, and invoke directly via the HTTP bridge. Tools are served over real JSON-RPC 2.0 / stdio transport.
🌀 SSE Stream (/Stream)
Server-Sent Events alternative to SignalR. Run a ReactAgent query and receive step-by-step updates directly in the browser via native EventSource API — no WebSocket overhead.
⚙ Workflows (/Workflow)
Manually fire inventory_low or sla_breach triggers with custom payloads. The WorkflowEngine dispatches matching workflows: ReorderWorkflow creates POs; SlaBreachWorkflow escalates tickets.
📋 Audit Log (/Audit)
In-memory audit trail for all tool calls, agent runs, webhook events, and auth actions. Timestamped entries show user, tenant, action, resource, and success/failure status.
🚀 Integration Hub (/Integration)
Live carrier rate quotes (FedEx, UPS, USPS, DHL — simulated) and SAP-compatible ERP actions via the real erp_connector MCP tool: create_po, get_po, post_gr, get_financials — all zero-config.
📄 Upload (/Upload)
Drag-and-drop .txt documents into the knowledge base. New content is immediately indexed and searchable via all RAG and Agent endpoints.
📊 Evaluation (/Evaluation)
Run Precision@K, Recall@K, MRR, and Hit Rate evaluation against 6 domain-specific test cases. Per-case breakdown table shows retrieval quality for each supply chain topic.
🧠 Memory (/Memory)
Add memory entries with importance scores and keywords. Search returns results ranked by keyword relevance (50%) + importance (30%) + recency decay (20%). Auto-summarize top entries. MemoryConsolidator merges similar entries via Jaccard similarity.
📈 Dashboard (/)
At-a-glance KPIs: document count, tool count, memory entries. Quick-start links to all features with feature summary and architectural overview.
MCP Tools Reference
All 9 tools are hosted in the NexaFlowDemo.McpServer project as static classes annotated with [McpServerToolType] and [McpServerTool]. The WithToolsFromAssembly() call auto-registers them all at server startup. The web app calls them through RealMcpClientService via real JSON-RPC 2.0 over stdio.
| Tool | Category | Parameters | Description |
|---|---|---|---|
inventory_lookup | Supply Chain | sku, category, status | Real-time stock levels for 6 NexaFlow SKUs; filter by status (Critical Low, Low Stock, In Stock) |
supplier_lookup | Supply Chain | id, name, country, status | Supplier directory with ratings, lead times, and approval status for 6 suppliers |
shipment_tracker | Supply Chain | tracking_number, order_id | Track 5 shipments (FedEx, UPS, DHL, Ocean) by NF-TRK ID or carrier number |
document_search | Knowledge | query, top_k | Semantic TF-IDF search over the NexaFlow knowledge base (calls back into Web's IDocumentStore) |
calculator | Utility | expression | Arithmetic expressions with recursive-descent parser (+, -, *, /, %, parentheses) |
datetime | Utility | timezone | Current UTC time or converted to any IANA timezone |
web_search | Research | query, max_results | Simulated logistics and supply chain search results (3 topic buckets) |
code_interpreter | Compute | code, timeout | Sandboxed C# evaluation via Roslyn CSharpScript; System namespaces only |
erp_connector | Integration | action, supplier_id, amount, po_number, quantity | SAP-compatible ERP: create_po, get_po, post_gr, get_financials. Full PO lifecycle simulation. |
MCP HTTP bridge: GET /api/mcp/tools — list all tools via RealMcpClientService • POST /api/mcp/invoke body: {"tool":"inventory_lookup","input":{"sku":"NF-SKU-001"}}
File Map
NexaFlowDemo.Core — Stage 1
| Path | Purpose |
|---|---|
Models/AgentModels.cs | AgentStep, AgentRunRequest, AgentStepType enum |
Models/Document.cs | Document, DocumentChunk, SearchResult |
Models/McpModels.cs | McpToolDefinition, McpInvokeResponse (bridge types) |
LLM/ILlmService.cs | LLM abstraction interface |
LLM/SimulatedLlmService.cs | Zero-key simulated LLM; used by unit tests only |
LLM/ClaudeService.cs | Live Anthropic API — always active in MCP Version |
Embeddings/SimulatedDenseEmbeddingService.cs | 384-dim FNV-1a hash embeddings |
VectorStore/InMemoryVectorStore.cs | Thread-safe in-memory cosine similarity |
VectorStore/SqliteVectorStore.cs | Persistent SQLite vector store |
VectorStore/VectorDocumentStore.cs | IDocumentStore over dense embeddings |
RAG/TextChunker.cs | Sliding window word chunker |
RAG/TfIdfEmbeddingService.cs | TF-IDF vectorizer + cosine similarity |
RAG/DocumentStore.cs | InMemoryDocumentStore (TF-IDF search) |
RAG/RagPipeline.cs | Retrieve → Augment → Generate |
RAG/RagEvaluator.cs | Precision@K, Recall@K, MRR, Hit Rate |
MCP/IMcpServerService.cs | MCP client abstraction (ListToolsAsync, InvokeToolAsync) |
Agents/ReactAgent.cs | ReAct loop: Think → Tool → Observe |
Agents/MultiAgentOrchestrator.cs | Research → Summary two-stage pipeline |
Memory/InMemoryMemoryStore.cs | Recency decay + importance scoring |
Conversation/InMemoryConversationStore.cs | Per-session conversation history |
Data/KnowledgeBaseSeeder.cs | Seeds 6 NexaFlow supply-chain documents |
NexaFlowDemo.Core — Stages 2/3/4
| Path | Purpose |
|---|---|
Agents/ParallelReactAgent.cs | Parallel tool execution via <tool_calls> + Task.WhenAll |
Agents/PlanningAgent.cs | Plan-then-execute: <plan><step> format → sequential tool loop |
Memory/MemoryConsolidator.cs | Jaccard similarity → merge near-duplicate memory entries |
Persistence/SqliteConversationStore.cs | SQLite-backed IConversationStore with sessions + turns tables |
Persistence/PersistenceOptions.cs | UseInMemory flag + ConnectionString config |
Workflows/IWorkflow.cs | IWorkflow interface + WorkflowContext + WorkflowResult |
Workflows/WorkflowEngine.cs | Event-driven trigger → matching workflow dispatch |
Workflows/ReorderWorkflow.cs | Trigger: inventory_low → creates simulated PO |
Workflows/SlaBreachWorkflow.cs | Trigger: sla_breach → escalation ticket + credit memo |
MultiTenant/ITenantContext.cs | TenantId, TenantName, AllowedRoles interface |
MultiTenant/TenantContext.cs | Implementation + TenantRegistry (Acme, Globex, Initech) |
MultiTenant/TenantScopedDocumentStore.cs | Wraps IDocumentStore; filters by tenantId metadata |
Audit/AuditEntry.cs | Audit record: Id, Timestamp, UserId, TenantId, Action, Resource |
Audit/IAuditLogger.cs | LogAsync + GetEntriesAsync interface |
Audit/InMemoryAuditLogger.cs | Thread-safe in-memory audit store |
Integration/ICarrierApiService.cs | GetRates, CreateShipment, TrackShipment interface |
Integration/CarrierModels.cs | RateRequest, RateQuote, ShipmentRequest/Response, TrackingResult |
Integration/SimulatedCarrierApiService.cs | 4-carrier rate simulation; shipment tracking lifecycle |
Integration/WebhookModels.cs | OrderWebhookEvent + OrderLineItem |
▶ NexaFlowDemo.McpServer (NEW — MCP Version)
| Path | Purpose |
|---|---|
Program.cs | Builds MCP stdio server: AddMcpServer().WithStdioServerTransport().WithToolsFromAssembly(…); suppresses stdout logging to protect JSON-RPC stream |
NexaFlowDemo.McpServer.csproj | References ModelContextProtocol v1.3.0; OutputType=Exe; references NexaFlowDemo.Core |
Tools/InventoryTools.cs | [McpServerToolType] static class; inventory_lookup — 6-SKU catalog |
Tools/SupplierTools.cs | supplier_lookup — 6-supplier directory with ratings and lead times |
Tools/ShipmentTools.cs | shipment_tracker — 5-shipment tracking system |
Tools/DocumentSearchTools.cs | document_search — TF-IDF search over seeded knowledge base |
Tools/CalculatorTools.cs | calculator — recursive-descent arithmetic parser |
Tools/DateTimeTools.cs | datetime — UTC + IANA timezone conversion |
Tools/WebSearchTools.cs | web_search — simulated logistics search (3 topic buckets) |
Tools/CodeInterpreterTools.cs | code_interpreter — Roslyn CSharpScript sandboxed executor |
Tools/ErpTools.cs | erp_connector — SAP-compatible ERP: create_po, get_po, post_gr, get_financials |
NexaFlowDemo.Web
| Path | Purpose |
|---|---|
Program.cs | DI wiring — 1 line replaces 10 old MCP registrations: AddSingleton<IMcpServerService, RealMcpClientService> |
appsettings.json | LLM/Embeddings/Agent/Persistence/Carrier config + McpServer section (ServerPath auto-detected) |
Services/RealMcpClientService.cs | Implements IMcpServerService via McpClient.CreateAsync + StdioClientTransport; spawns McpServer subprocess; SemaphoreSlim lazy-init |
Services/IMcpServerService.cs | ListToolsAsync / InvokeToolAsync abstraction (same interface, real transport) |
Services/McpServerOptions.cs | ServerPath config binding for McpServer executable location |
Services/UserService.cs | IUserService + DemoUserService; SHA-256 hashed passwords; 4 demo users |
Controllers/HomeController.cs | Dashboard KPIs |
Controllers/ChatController.cs | RAG Chat API |
Controllers/AgentController.cs | Single-agent REST API |
Controllers/McpController.cs | MCP HTTP bridge: GET /tools, POST /invoke (via RealMcpClientService) |
Controllers/OrchestrationController.cs | Pipeline page host |
Controllers/UploadController.cs | Document upload + list API |
Controllers/EvaluationController.cs | RAG evaluation run + test-case builder |
Controllers/MemoryController.cs | Add / Search / Delete / Summarize memory |
Controllers/AuthController.cs | Cookie login/logout; validates against DemoUserService |
Controllers/WorkflowController.cs | Workflow list + manual trigger form |
Controllers/StreamController.cs | SSE endpoint: GET /stream/run?query=… |
Controllers/AuditController.cs | Audit log viewer |
Controllers/IntegrationController.cs | Carrier rate quotes + ERP action form |
Controllers/WebhookController.cs | POST /api/webhooks/orders — order event ingestion |
Hubs/AgentHub.cs | SignalR: streams ReactAgent steps |
Hubs/OrchestrationHub.cs | SignalR: streams pipeline steps |
Views/Home/Index.cshtml | Dashboard with feature cards |
Views/Chat/Index.cshtml | Chat UI with source document panel |
Views/Agent/Index.cshtml | Step-by-step agent run visualizer |
Views/Orchestration/Index.cshtml | Real-time pipeline with agent badges |
Views/Mcp/Index.cshtml | Tool browser + JSON invoke console |
Views/Upload/Index.cshtml | Drag-and-drop document uploader |
Views/Evaluation/Index.cshtml | Evaluation metrics dashboard |
Views/Memory/Index.cshtml | Memory CRUD + search + summarize |
Views/Auth/Login.cshtml | Login form; shows demo credentials |
Views/Workflow/Index.cshtml | Registered workflows list + manual trigger form |
Views/Stream/Index.cshtml | SSE streaming demo page |
Views/Audit/Index.cshtml | Audit log table view |
Views/Integration/Index.cshtml | Carrier rate quote + ERP console |
Views/Shared/_Layout.cshtml | Nav bar with all 12 pages + user/role display |
Test Suite — 181 Tests Passing
| Test Class | Count | What It Tests |
|---|---|---|
| Core Tests — Stage 1 (95 Tests) | ||
RAG/TextChunkerTests | 5 | Sliding window chunker: empty, short, long, overlap, null |
RAG/DocumentStoreTests | 5 | InMemoryDocumentStore: add, search, delete, topK, empty query |
RAG/RagPipelineTests | 4 | RagPipeline: answer generation, empty store, source results, ranking |
RAG/RagEvaluatorTests | 5 | RagEvaluator: no cases, valid cases, metric bounds, detail count, multi-relevant |
Embeddings/SimulatedDenseEmbeddingServiceTests | 7 | Dimensions, normalization, determinism, diff texts, batch, empty string |
VectorStore/InMemoryVectorStoreTests | 6 | Upsert, search topK, descending scores, delete by doc, overwrite, empty |
VectorStore/SqliteVectorStoreTests | 5 | In-memory SQLite: upsert, search, delete, embedding roundtrip, empty |
MCP/McpToolDefinitionTests | 5 | McpToolDefinition model: name, description, parameter schema, required fields, JSON roundtrip |
Agents/ReactAgentTests | 7 | Final answer, thinking step, tool call+result, cancellation, system prompt, content, max iterations |
Agents/MultiAgentOrchestratorTests | 5 | Handoff steps, final answer, both agents, cancellation, handoff message content |
Memory/InMemoryMemoryStoreTests | 8 | Add/get, search relevance, delete, delete missing, summarize, empty summarize, access count, topK |
Conversation/InMemoryConversationStoreTests | 7 | Create, get, get null, list, add turn, delete, delete missing |
LLM/ClaudeServiceTests | 6 | SplitPrompt extraction, system param pass-through, HTTP header wiring, non-2xx error handling, response mapping, cancellation |
| Core Tests — Stages 2/3/4 (59 Tests) | ||
Persistence/SqliteConversationStoreTests | 7 | SQLite sessions/turns: create, get, null, list, add turn, delete, multi-turn ordering |
Agents/ParallelReactAgentTests | 5 | Direct final answer, single tool call, parallel tool calls (Task.WhenAll), thinking step, LLM error |
Agents/PlanningAgentTests | 4 | No plan → direct answer, 2-step plan with synthesis, tool call in step, LLM error |
Workflows/WorkflowEngineTests | 4 | No match, matching trigger, only matching fires, faulting workflow doesn't block others |
Workflows/ReorderWorkflowTests | 4 | Creates PO, default payload, trigger name, unique PO numbers |
Memory/MemoryConsolidatorTests | 8 | Empty list, single entry, similar entries merged, dissimilar kept, keyword union, Jaccard identical/disjoint/partial |
MultiTenant/TenantContextTests | 5 | Registry has 3 tenants, Acme correct, FindById success, FindById unknown, AllowedRoles set |
MultiTenant/TenantScopedDocumentStoreTests | 3 | Tags docs with tenantId, GetAll filters to tenant, Search filters to tenant |
Audit/InMemoryAuditLoggerTests | 5 | Store entry, most-recent-first, respects limit, multiple entries, default Success=true |
Integration/CarrierApiServiceTests | 6 | Returns 4 carriers, heavier costs more, tracking number, track created shipment, unknown tracking, unique numbers |
Integration/ErpConnectorToolTests | 7 | (Legacy Core ERP tests) create_po, missing supplier, get_po, post_gr, get_financials, unknown action, definition name |
| McpServer Tool Tests — 27 New Tests (MCP Version) | ||
McpServer/InventoryToolsTests | 5 | Valid SKU lookup, invalid SKU, no params, filter by status, all-SKU listing |
McpServer/SupplierToolsTests | 4 | Valid ID, invalid ID, name search, status/country filter |
McpServer/ShipmentToolsTests | 3 | Valid tracking number, by order ID, not found |
McpServer/CalculatorToolsTests | 4 | Basic arithmetic, parentheses, percent operator, invalid expression |
McpServer/DateTimeToolsTests | 3 | UTC result, valid timezone conversion, invalid timezone |
McpServer/WebSearchToolsTests | 3 | Valid query returns results, max_results respected, logistics topic bucket |
McpServer/ErpToolsTests | 5 | create_po success, missing supplier fails, get_po after create, post_gr updates status, unknown action |
Quick Start
Prerequisites
- .NET 9 SDK
- Visual Studio 2022 (17.8+) or VS Code with C# Dev Kit
- Anthropic API key (required — MCP Version always uses live Claude)
- No database server, no broker, no other external dependencies
Set Your API Key
# In src/NexaFlowDemo.Web/appsettings.json:
{
"LLM": {
"ApiKey": "sk-ant-...",
"Model": "claude-sonnet-4-6"
}
}
Run
cd src/NexaFlowDemo.Web dotnet run # → http://localhost:5xxx (check console for port) # The McpServer subprocess is launched automatically on first tool call — no manual start needed
Test
# Run all tests (Core + McpServer tool tests) dotnet test src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj # → Passed! Failed: 0, Passed: 181 # Run with minimal output (final verification) dotnet test src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj --verbosity minimal
Demo Login Accounts
| Username | Password | Role | Tenant |
|---|---|---|---|
admin | admin123 | Admin | ACME Distribution |
ops | ops123 | Ops | ACME Distribution |
manager | manager123 | Manager | Globex Corp |
exec | exec123 | Exec | Initech Logistics |
Try the Workflow Engine
# POST /api/webhooks/orders (triggers inventory_low when quantity > 50)
curl -X POST http://localhost:5xxx/api/webhooks/orders \
-H "Content-Type: application/json" \
-d '{"orderId":"ORD-001","customerId":"CUST-001","source":"Shopify","status":"new","totalAmount":1500,"lineItems":[{"skuId":"NF-SKU-001","quantity":100,"price":15}]}'
Enable SQLite Conversation Persistence
{
"Persistence": {
"UseInMemory": false,
"ConnectionString": "Data Source=nexaflow.db"
}
}
Then change Program.cs to register SqliteConversationStore instead of InMemoryConversationStore.
MCP API Examples
# List tools (fetched from McpServer subprocess via RealMcpClientService)
curl http://localhost:5xxx/api/mcp/tools
# ERP: Create PO
curl -X POST http://localhost:5xxx/api/mcp/invoke \
-H "Content-Type: application/json" \
-d '{"tool":"erp_connector","input":{"action":"create_po","supplier_id":"SUP-001","amount":"5000"}}'
# Inventory lookup
curl -X POST http://localhost:5xxx/api/mcp/invoke \
-H "Content-Type: application/json" \
-d '{"tool":"inventory_lookup","input":{"sku":"NF-SKU-001"}}'
Roadmap
| Stage | Features | Status |
|---|---|---|
| Stage 1 Core Platform |
Core library (Models, LLM, Embeddings, VectorStore, RAG, MCP, Agents, Memory, Conversation, Data) • 8 MCP tools (3 supply-chain domain tools) • ReactAgent ReAct loop • Multi-Agent Orchestrator • Web app (8 controllers, 2 SignalR hubs, 8 views) • Knowledge base seeder (6 supply-chain docs) • 95 tests • HTML roadmap | Complete ✓ |
| Stage 2 Persistence & New Agents |
SQLite conversation persistence (SqliteConversationStore) • ParallelReactAgent with Task.WhenAll parallel tool calls • PlanningAgent (plan→execute→synthesize) • ERP Connector tool (create_po, get_po, post_gr, get_financials) • Persistence config options • 7 new tests |
Complete ✓ |
| Stage 3 Advanced Agents & Workflows |
SSE streaming endpoint (/stream/run) • MemoryConsolidator (Jaccard keyword merge) • WorkflowEngine event-driven trigger dispatch • ReorderWorkflow (inventory_low → PO) • SlaBreachWorkflow (sla_breach → escalation + credit) • Workflow controller + view • SSE view • 21 new tests |
Complete ✓ |
| Stage 4 Production Features |
Multi-tenant: TenantRegistry (Acme, Globex, Initech) + TenantScopedDocumentStore • Cookie authentication + 4 demo users (admin/ops/manager/exec) • IAuditLogger / InMemoryAuditLogger • Carrier API service (4 carriers, SimulationMode) • Webhook order event ingestion (POST /api/webhooks/orders) • Audit/Integration views • User display in nav • 31 new tests |
Complete ✓ |
| MCP Version Real MCP Protocol |
New NexaFlowDemo.McpServer project (console app, stdio server) • 9 tool classes with [McpServerToolType]/[McpServerTool] attributes • WithToolsFromAssembly auto-registration • RealMcpClientService (McpClient.CreateAsync + StdioClientTransport) replaces all simulated MCP wiring • 27 new McpServer tool tests • 181 total tests passing • Official ModelContextProtocol v1.3.0 NuGet package • JSON-RPC 2.0 over stdio |
Complete ✓ |
ClaudeLiveKey → MCP_Version Migration
This project (MCP_Version) is a copy of the completed ClaudeLiveKey build with the simulated MCP layer replaced by real Model Context Protocol using the official ModelContextProtocol v1.3.0 NuGet package, JSON-RPC 2.0 over stdio transport, and a new NexaFlowDemo.McpServer subprocess.
What Changed
| File / Project | Change | Why |
|---|---|---|
NexaFlowDemo.McpServer (new project) |
New console app project. Program.cs calls:builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly(
Assembly.GetExecutingAssembly(), jsonOptions);
await app.RunAsync();
9 [McpServerToolType] static tool classes in Tools/. Stdout logging suppressed so JSON-RPC stream is uncontaminated.
|
Encapsulates all tool logic in a real MCP stdio server; any MCP-compatible client can now use these tools. |
Web/Services/RealMcpClientService.cs (new file) |
Implements IMcpServerService using the official SDK:var transport = new StdioClientTransport(
new StdioClientTransportOptions {
Command = "dotnet",
Arguments = ["exec", ResolveServerPath()],
Name = "NexaFlowDemoMcpServer"
});
_client = await McpClient.CreateAsync(transport, ct);
SemaphoreSlim guards lazy init for thread safety. ResolveServerPath() auto-detects sibling project bin output.
|
Replaces all 10 hand-written tool registrations with a single JSON-RPC bridge to the McpServer subprocess. |
Web/Program.cs |
10 individual AddSingleton<IMcpTool, XyzTool>() lines replaced by 1:builder.Services.AddSingleton<IMcpServerService,
RealMcpClientService>();
|
The McpServer project owns all tool implementations; Web only needs the client bridge. |
McpServer/Tools/*.cs |
Old IMcpTool-based classes replaced by [McpServerToolType] static classes:[McpServerToolType]
public static class InventoryTools
{
[McpServerTool(Name = "inventory_lookup", ReadOnly = true)]
[Description("Look up stock level for a SKU.")]
public static string LookupInventory(
[Description("The product SKU code")] string sku)
{ … }
}
No interface to implement; schema generated from [Description] attributes automatically.
|
Official SDK pattern; eliminates manual schema JSON; tools can be static (no DI needed). |
appsettings.json |
Added McpServer section:"McpServer": {
"ServerPath": "" // auto-detected if blank
}
Removed LLM.SimulationMode (always live in MCP Version).
|
ServerPath allows explicit override when the auto-detection path differs from the default. |
NexaFlowDemo.Tests |
27 new McpServer tool test classes added (InventoryToolsTests, SupplierToolsTests, ShipmentToolsTests, CalculatorToolsTests, DateTimeToolsTests, WebSearchToolsTests, ErpToolsTests). All tests call the static tool methods directly — no McpClient needed in tests. | Static tool methods are trivially unit-testable without protocol overhead; 27 new tests → 181 total. |
What Did NOT Change
- All 154 Core unit tests — no changes; they test Core library components, not MCP transport.
- SimulatedLlmService — kept in Core library; still used by agent unit tests.
- Embeddings — remain simulated (
SimulatedDenseEmbeddingService); Anthropic embedding API not required for this demo. - All workflows, memory, multi-tenant, audit, carrier services — all simulated, no external calls.
- All Razor views and controllers — unchanged; they use
IMcpServerServicewhich the newRealMcpClientServiceimplements. - ClaudeService — unchanged; the MCP Version was already using the live Claude API.
How to Set Your API Key
Get your key from console.anthropic.com → API Keys, then set it in one of these ways:
| Method | How | Best For |
|---|---|---|
| appsettings.Development.json | Create src/NexaFlowDemo.Web/appsettings.Development.json with {"LLM":{"ApiKey":"sk-ant-..."}} |
Local development (override file, gitignored) |
| Environment variable | Set LLM__ApiKey=sk-ant-... before running (double underscore = config section separator) |
CI/CD, Docker, production |
| appsettings.json | Replace the placeholder directly in src/NexaFlowDemo.Web/appsettings.json |
Quick local test (do not commit) |
Bug Fixes & API Gotchas (MCP Version)
🐞 BF-001 — StdioClientTransportOptions Has No Version Property
| Field | Detail |
|---|---|
| Symptom | Compiler error: "StdioClientTransportOptions does not contain a definition for 'Version'" |
| Root Cause | The ModelContextProtocol v1.3.0 SDK's StdioClientTransportOptions record has Command, Arguments, Name, and WorkingDirectory — no Version property. |
| Fix | // WRONG:
new StdioClientTransportOptions {
Command = "dotnet",
Arguments = ["exec", path],
Name = "NexaFlowDemoMcpServer",
Version = "1.0" // ← does not exist
}
// CORRECT:
new StdioClientTransportOptions {
Command = "dotnet",
Arguments = ["exec", path],
Name = "NexaFlowDemoMcpServer"
} |
🐞 BF-002 — McpClientTool.JsonSchema Not InputSchema
| Field | Detail |
|---|---|
| Symptom | Compiler error or null reference when reading tool schema. |
| Root Cause | The correct property on McpClientTool is JsonSchema. InputSchema does not exist in v1.3.0. |
| Fix | // WRONG: var schema = tool.InputSchema; // CORRECT: var schema = tool.JsonSchema; |
🐞 BF-003 — ListToolsAsync Returns a List Directly (No .Tools Wrapper)
| Field | Detail |
|---|---|
| Symptom | Compiler error: "ValueTask<IList<McpClientTool>> does not contain a definition for 'Tools'" |
| Root Cause | McpClient.ListToolsAsync() in v1.3.0 returns ValueTask<IList<McpClientTool>> — the list is the result directly, not a wrapper object with a .Tools property. |
| Fix | // WRONG:
var response = await _client!.ListToolsAsync(ct);
foreach (var tool in response.Tools) { … }
// CORRECT:
var tools = await _client!.ListToolsAsync(ct);
foreach (var tool in tools) { … } |
🐞 BF-004 — McpServer Must Suppress Stdout Logging
| Field | Detail |
|---|---|
| Symptom | RealMcpClientService throws JSON parse errors or hangs on startup. McpServer subprocess crashes immediately. |
| Root Cause | The JSON-RPC 2.0 stdio transport uses stdout exclusively for protocol messages. Any logging written to stdout (the default for console apps) corrupts the stream, causing the client to receive malformed JSON. |
| Fix | In McpServer/Program.cs, redirect all logging to stderr and suppress console output:builder.Logging.ClearProviders();
builder.Logging.AddConsole(opts =>
{
opts.LogToStandardErrorThreshold = LogLevel.Trace;
});
This ensures stdout carries only clean JSON-RPC messages. |
| General Rule | Any MCP stdio server must write ALL logging to stderr. stdout is reserved for the JSON-RPC protocol stream. |
CLI Build Reference
All commands below run from the solution root C:\Guy\Notes\Claud Projects\Agentic Baiic 03_SupplyChain_ClaudeLiveKey_MCP_Version\. This is a 4-project solution — restore and build all four projects.
Restore Dependencies
dotnet restore src/NexaFlowDemo.Core/NexaFlowDemo.Core.csproj dotnet restore src/NexaFlowDemo.McpServer/NexaFlowDemo.McpServer.csproj dotnet restore src/NexaFlowDemo.Web/NexaFlowDemo.Web.csproj dotnet restore src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj
Build Individual Projects
# Build the core library only dotnet build src/NexaFlowDemo.Core/NexaFlowDemo.Core.csproj # Build the McpServer project (also rebuilds Core) dotnet build src/NexaFlowDemo.McpServer/NexaFlowDemo.McpServer.csproj # Build the web app (also rebuilds Core; McpServer built separately) dotnet build src/NexaFlowDemo.Web/NexaFlowDemo.Web.csproj # Build the test project (rebuilds Core and McpServer as dependencies) dotnet build src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj
Run Tests
# Build + run all tests (Core tests + McpServer tool tests = 181) dotnet test src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj # Run with minimal output (used for final verification) dotnet test src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj --verbosity minimal # Run without rebuilding (re-run against already-compiled binaries) dotnet test src/NexaFlowDemo.Tests/NexaFlowDemo.Tests.csproj --no-build
Run the Web App
# Start the web application dotnet run --project src/NexaFlowDemo.Web/NexaFlowDemo.Web.csproj # → http://localhost:5xxx (check console for port) # → McpServer subprocess is spawned automatically on first tool call
Inspect Project Files
# List all .cs files in a project (PowerShell) Get-ChildItem "src/NexaFlowDemo.Core" -Recurse -Filter "*.cs" ` | Select-Object -ExpandProperty FullName | Sort-Object Get-ChildItem "src/NexaFlowDemo.McpServer" -Recurse -Filter "*.cs" ` | Select-Object -ExpandProperty FullName | Sort-Object Get-ChildItem "src/NexaFlowDemo.Web" -Recurse -Filter "*.cs" ` | Select-Object -ExpandProperty FullName | Sort-Object Get-ChildItem "src/NexaFlowDemo.Tests" -Recurse -Filter "*.cs" ` | Select-Object -ExpandProperty FullName | Sort-Object
Build Sequence Used During Development
| Step | Command | Purpose |
|---|---|---|
| 1 | dotnet build NexaFlowDemo.Core.csproj | Verify Core compiles after each batch of changes |
| 2 | dotnet build NexaFlowDemo.McpServer.csproj | Verify McpServer compiles after adding or editing tool classes |
| 3 | dotnet build NexaFlowDemo.Web.csproj | Verify Web compiles after adding RealMcpClientService and Program.cs changes |
| 4 | dotnet test NexaFlowDemo.Tests.csproj | Full build + test run; target was 0 failures at 181 tests |
| 5 | dotnet test … --verbosity minimal | Final clean pass — shows only the summary line |
NuGet Packages Added (MCP Version — beyond ClaudeLiveKey)
| Package | Version | Used By |
|---|---|---|
ModelContextProtocol | 1.3.0 | NexaFlowDemo.McpServer (server) + RealMcpClientService (client) |
Microsoft.Extensions.Hosting | 9.0.0 | NexaFlowDemo.McpServer (IHost for stdio server) |
All packages from ClaudeLiveKey (Microsoft.Data.Sqlite, Microsoft.CodeAnalysis.CSharp.Scripting, Moq, xunit, etc.) are unchanged.