Skip to content

Commit c1b5dd5

Browse files
committed
vertical slice
1 parent 8157609 commit c1b5dd5

133 files changed

Lines changed: 3695 additions & 1147 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.csharpierignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# These files intentionally stay on the repository's dotnet-format whitespace layout.
22
Directory.Build.props
33
Directory.Packages.props
4-
src/ManagedCode.MCPGateway/Configuration/McpGatewayOptions.cs
5-
src/ManagedCode.MCPGateway/Internal/Catalog/Sources/McpGatewayToolSourceRegistrations.cs
6-
src/ManagedCode.MCPGateway/Internal/Runtime/Catalog/McpGatewayRuntime.Indexing.cs
4+
src/ManagedCode.MCPGateway/Gateway/Configuration/McpGatewayOptions.cs
5+
src/ManagedCode.MCPGateway/Catalog/Internal/Sources/McpGatewayToolSourceRegistrations.cs
6+
src/ManagedCode.MCPGateway/Catalog/Internal/Runtime/McpGatewayRuntime.Indexing.cs

AGENTS.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ If no new rule is detected -> do not update the file.
9191
- format: `dotnet format ManagedCode.MCPGateway.slnx`
9292
- format-check: `dotnet format ManagedCode.MCPGateway.slnx --verify-no-changes`
9393
- roslynator-analyze: `dotnet tool run roslynator analyze src/ManagedCode.MCPGateway/ManagedCode.MCPGateway.csproj tests/ManagedCode.MCPGateway.Tests/ManagedCode.MCPGateway.Tests.csproj`
94-
- coverage: `dotnet tool run coverlet tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests.dll --target "dotnet" --targetargs "test --solution ManagedCode.MCPGateway.slnx -c Release --no-build" --format cobertura --output artifacts/coverage/coverage.cobertura.xml`
94+
- coverage: `dotnet tool run coverlet tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests.dll --target "./tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests" --targetargs "" --format cobertura --output artifacts/coverage/coverage.cobertura.xml`
9595
- coverage-report: `dotnet tool run reportgenerator -reports:"artifacts/coverage/coverage.cobertura.xml" -targetdir:"artifacts/coverage-report" -reporttypes:"HtmlSummary;MarkdownSummaryGithub"`
9696

9797
### Rule Precedence
@@ -164,17 +164,16 @@ If no new rule is detected -> do not update the file.
164164

165165
- `src/ManagedCode.MCPGateway/` contains the package source.
166166
- `tests/ManagedCode.MCPGateway.Tests/` contains integration-style package tests.
167-
- `src/ManagedCode.MCPGateway/Abstractions/` contains public interfaces, grouped by concern when needed.
168-
- `src/ManagedCode.MCPGateway/Configuration/` contains public configuration types and service keys.
169-
- `src/ManagedCode.MCPGateway/Models/` contains public contracts grouped by behavior such as search, invocation, catalog, and embeddings.
170-
- `src/ManagedCode.MCPGateway/Embeddings/` contains public embedding-store implementations.
171-
- `src/ManagedCode.MCPGateway/Internal/` contains internal catalog, runtime, and helper implementation details.
172-
- `src/ManagedCode.MCPGateway/Registration/` contains DI registration extensions.
173-
- `src/ManagedCode.MCPGateway/McpGateway.cs` is the public gateway facade.
174-
- `src/ManagedCode.MCPGateway/Internal/Runtime/` contains the internal runtime orchestration implementation, grouped by core, catalog, search, invocation, and embeddings responsibilities.
175-
- `src/ManagedCode.MCPGateway/McpGatewayToolSet.cs` exposes the gateway as reusable `AITool` meta-tools.
167+
- `src/ManagedCode.MCPGateway/Gateway/` contains the public gateway facade, factory, options, DI entry points, runtime core, telemetry, and serialization.
168+
- `src/ManagedCode.MCPGateway/Discovery/` contains reusable `AITool` meta-tools, auto-discovery chat integration, and discovery-specific registration/configuration.
169+
- `src/ManagedCode.MCPGateway/Catalog/` contains catalog contracts, models, registration state, descriptors, source adapters, and index-building logic.
170+
- `src/ManagedCode.MCPGateway/Search/` contains search contracts, models, caching, embeddings, and internal ranking/graph/normalization/context logic.
171+
- `src/ManagedCode.MCPGateway/Invocation/` contains invocation models and runtime execution helpers.
172+
- `src/ManagedCode.MCPGateway/Prompts/` contains prompt contracts, models, and prompt catalog implementation.
173+
- `src/ManagedCode.MCPGateway/Hosting/` contains MCP server export and warmup integration.
176174
- `.codex/skills/` contains repo-local MCAF skills for Codex.
177175
- Keep the source tree explicitly modular: separate public API folders from `Internal/` implementation folders, and group runtime classes by responsibility in dedicated folders instead of dumping search, indexing, invocation, registry, and infrastructure files into the package root, because flat structure hides boundaries and invites god-object design.
176+
- Prefer vertical-slice package structure for non-trivial areas: group code by feature and subfeature with isolated supporting types nearby instead of accumulating broad cross-cutting buckets that mix unrelated behaviors in one folder, because feature-local structure keeps boundaries auditable.
178177

179178
### Skills (ALL TASKS)
180179

@@ -211,14 +210,16 @@ If no new rule is detected -> do not update the file.
211210
- Test framework in this repository is TUnit. Never add or keep xUnit here.
212211
- This repository uses `TUnit` on `Microsoft.Testing.Platform`; never use VSTest-only flags such as `--filter` or `--logger`, because they are not supported here.
213212
- For TUnit solution runs, always invoke `dotnet test --solution ...`; do not pass the solution path positionally.
214-
- Coverage in this repository uses the local `coverlet.console` tool against the built test assembly, then renders human-readable output with the local `reportgenerator` tool.
213+
- Coverage in this repository uses the local `coverlet.console` tool against the built test assembly and must target the built `ManagedCode.MCPGateway.Tests` TUnit application directly; routing coverage through `dotnet test --solution ...` under `Microsoft.Testing.Platform` reports zero hits in this repo.
215214
- Every behavior change must include or update tests in `tests/ManagedCode.MCPGateway.Tests/`.
216215
- Add tests only when they close a meaningful behavior or regression gap; avoid low-signal tests that only increase count without improving confidence.
217216
- Keep tests focused on real gateway behavior:
218217
- local tool indexing and invocation
219218
- MCP tool indexing and invocation
220219
- vector search behavior
221220
- Markdown-LD graph search and vector-to-graph fallback behavior
221+
- For MCP protocol coverage, prefer end-to-end integration tests that use the official `ModelContextProtocol` C# SDK on both client and server paths instead of test doubles when the behavior depends on real protocol contracts, because MCP regressions often hide outside mocked flows.
222+
- When changing MCP discovery, routing, search, or invocation behavior, add multi-step integration cases with realistic tool metadata, side-effect boundaries, and client-driven execution so retrieval and invocation are verified under production-like conditions.
222223
- Keep embedding-based search covered with deterministic local tests by using a fake or test-only embedding generator.
223224
- Keep request context behavior covered when search or invocation consumes contextual inputs.
224225
- Do not remove tests to get green builds.
@@ -307,6 +308,7 @@ If no new rule is detected -> do not update the file.
307308
### Likes
308309

309310
- Explicit package structure
311+
- Vertical-slice structure with feature and subfeature isolation
310312
- Reusable library design over app-specific glue
311313
- Search + execute flows covered by automated tests
312314
- Clean root packaging and CI setup

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
</PropertyGroup>
55
<ItemGroup>
66
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="2.0.2" />
7-
<PackageVersion Include="ManagedCode.MarkdownLd.Kb" Version="0.1.3" />
7+
<PackageVersion Include="ManagedCode.MarkdownLd.Kb" Version="0.1.5" />
88
<PackageVersion Include="Microsoft.Agents.AI" Version="1.1.0" />
99
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.5.0" />
1010
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.6" />
@@ -17,6 +17,6 @@
1717
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
1818
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.202" />
1919
<PackageVersion Include="ModelContextProtocol" Version="1.2.0" />
20-
<PackageVersion Include="TUnit" Version="1.37.0" />
20+
<PackageVersion Include="TUnit" Version="1.37.10" />
2121
</ItemGroup>
2222
</Project>

README.md

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dotnet add package ManagedCode.MCPGateway
2525
- one prompt catalog for MCP prompts aggregated across registered MCP sources
2626
- one downstream MCP server export path over the aggregated tool and prompt catalog
2727
- one search API with default Markdown-LD graph ranking, opt-in vector ranking, and vector-first `Auto`
28+
- one category-first routing API for advanced tool discovery flows
2829
- one invocation API for both local tools and MCP tools
2930
- additive catalog registration through `IMcpGatewayRegistry`
3031
- full in-memory catalog control through `IMcpGatewayCatalogRuntime`
@@ -74,6 +75,10 @@ await using var serviceProvider = services.BuildServiceProvider();
7475
var gateway = serviceProvider.GetRequiredService<IMcpGateway>();
7576

7677
var search = await gateway.SearchAsync("find github repositories");
78+
var route = await gateway.RouteToolsAsync(new McpGatewayToolRouteRequest(
79+
Query: "find github repositories",
80+
MaxCategories: 3,
81+
MaxToolsPerCategory: 2));
7782
var invoke = await gateway.InvokeAsync(new McpGatewayInvokeRequest(
7883
ToolId: search.Matches[0].ToolId,
7984
Query: "managedcode"));
@@ -244,7 +249,7 @@ var prompts = await gatewayHost.PromptCatalog.ListPromptsAsync();
244249

245250
Use the package surfaces like this:
246251

247-
- `IMcpGateway`: build, list, search, invoke
252+
- `IMcpGateway`: build, list, search, route, invoke
248253
- `IMcpGatewayRegistry`: additive tool and source registration
249254
- `IMcpGatewayCatalogRuntime`: full in-memory catalog clear or reconfiguration
250255
- `IMcpGatewayPromptCatalog`: list and render aggregated MCP prompts
@@ -315,7 +320,7 @@ var invoke = await gateway.InvokeAsync(new McpGatewayInvokeRequest(
315320

316321
## Search Hints
317322

318-
If a tool should be easier to find through multilingual aliases or stable domain keywords, register explicit search hints:
323+
If a tool should be easier to find through multilingual aliases, stable domain keywords, category-first routing, or execution-aware discovery, register explicit search hints:
319324

320325
```csharp
321326
services.AddMcpGateway(options =>
@@ -340,11 +345,35 @@ services.AddMcpGateway(options =>
340345
"alerts",
341346
"inbox",
342347
"mentions"
343-
]));
348+
],
349+
Categories: ["communications"],
350+
Tags: ["notifications", "activity-feed"],
351+
DataSources: ["inbox-api"],
352+
UsageExamples:
353+
[
354+
new McpGatewayToolExample(
355+
"show unread alerts",
356+
"{\"count\":3}",
357+
"Check whether the user has pending notifications."),
358+
new McpGatewayToolExample(
359+
"summarize mentions from the last release thread",
360+
"{\"mentions\":[\"alice\",\"bob\"]}",
361+
"Triage release-thread mentions before replying."),
362+
new McpGatewayToolExample(
363+
"list urgent notifications for the mobile workspace",
364+
"{\"alerts\":[{\"severity\":\"high\"}]}",
365+
"Filter alerts for a specific workspace and urgency level.")
366+
],
367+
ReadOnly: true,
368+
Idempotent: true,
369+
CostTier: McpGatewayToolCostTier.Low,
370+
LatencyTier: McpGatewayToolLatencyTier.Low));
344371
});
345372
```
346373

347-
Hints are included in descriptors, Markdown-LD graph documents, vector documents, and lexical boosts. They are the preferred way to improve multilingual discovery without hardcoded ranking exceptions.
374+
Hints are included in descriptors, Markdown-LD graph documents, vector documents, exported MCP metadata, and lexical boosts. They are the preferred way to improve multilingual discovery and category-aware routing without hardcoded ranking exceptions.
375+
376+
If a tool should stay out of default discovery until a host explicitly opts into it, set `EnabledByDefault: false`. Standard `SearchAsync(...)` and `RouteToolsAsync(...)` hide those tools unless the request sets `IncludeDisabledTools = true`.
348377

349378
## Search Strategies
350379

@@ -525,11 +554,33 @@ services.AddMcpGateway(options =>
525554
});
526555
```
527556

528-
## Meta-Tools And Chat Integration
557+
## Routing, Meta-Tools, And Chat Integration
558+
559+
For category-first discovery:
560+
561+
```csharp
562+
var route = await gateway.RouteToolsAsync(new McpGatewayToolRouteRequest(
563+
Query: "open a bridge and prepare a failover plan for incident 42",
564+
MaxCategories: 2,
565+
MaxToolsPerCategory: 2,
566+
ContextSummary: "incident already confirmed; the next step is an action tool",
567+
PreferReadOnly: false,
568+
IncludeDisabledTools: true));
569+
```
570+
571+
`McpGatewayToolRouteResult` returns:
572+
573+
- `Categories` with grouped tool candidates
574+
- `SuggestedMatches` with the flattened recommended tools
575+
- `Diagnostics` from the underlying search path
576+
- `RankingMode` from the underlying graph/vector/hybrid search
577+
578+
The router prefers safe read-only tools for discovery/inspection-style requests and uses cost and latency tiers as tie-breakers when tool quality is otherwise similar.
529579

530-
The gateway can expose itself as two reusable tools:
580+
The gateway can expose itself as three reusable tools:
531581

532582
- `gateway_tools_search`
583+
- `gateway_tools_route`
533584
- `gateway_tool_invoke`
534585

535586
From the gateway:
@@ -575,7 +626,7 @@ var response = await chatClient.GetResponseAsync(
575626
});
576627
```
577628

578-
This flow starts with the two gateway meta-tools and only projects the latest matching tools as direct proxy tools after search.
629+
This flow starts with the three gateway meta-tools and only projects the latest matching tools as direct proxy tools after search.
579630

580631
If the host already has search results and only wants the discovered proxy tools:
581632

@@ -655,6 +706,7 @@ Search telemetry includes configured strategy, ranking mode, graph/vector usage,
655706
- [ADR-0004: Process-local embedding store uses IMemoryCache](docs/ADR/ADR-0004-process-local-embedding-store-uses-imemorycache.md)
656707
- [ADR-0005: Markdown-LD graph search for tool retrieval](docs/ADR/ADR-0005-markdown-ld-graph-search-for-tool-retrieval.md)
657708
- [ADR-0006: Vector-first auto search and runtime telemetry](docs/ADR/ADR-0006-vector-first-auto-search-and-runtime-telemetry.md)
709+
- [ADR-0007: Vertical-slice package organization](docs/ADR/ADR-0007-vertical-slice-package-organization.md)
658710
- [Feature spec: Search query normalization and ranking](docs/Features/SearchQueryNormalizationAndRanking.md)
659711
- [Feature spec: Auto vector-first search and performance](docs/Features/AutoVectorFirstSearchAndPerformance.md)
660712

@@ -684,6 +736,6 @@ dotnet csharpier check .
684736
Coverage:
685737

686738
```bash
687-
dotnet tool run coverlet tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests.dll --target "dotnet" --targetargs "test --solution ManagedCode.MCPGateway.slnx -c Release --no-build" --format cobertura --output artifacts/coverage/coverage.cobertura.xml
739+
dotnet tool run coverlet tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests.dll --target "./tests/ManagedCode.MCPGateway.Tests/bin/Release/net10.0/ManagedCode.MCPGateway.Tests" --targetargs "" --format cobertura --output artifacts/coverage/coverage.cobertura.xml
688740
dotnet tool run reportgenerator -reports:"artifacts/coverage/coverage.cobertura.xml" -targetdir:"artifacts/coverage-report" -reporttypes:"HtmlSummary;MarkdownSummaryGithub"
689741
```

docs/ADR/ADR-0003-reusable-chat-client-and-agent-tool-modules.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Context
44

5-
`ManagedCode.MCPGateway` already exposes the gateway as two reusable meta-tools through `McpGatewayToolSet` and `IMcpGateway.CreateMetaTools(...)`.
5+
`ManagedCode.MCPGateway` already exposes the gateway as reusable meta-tools through `McpGatewayToolSet` and `IMcpGateway.CreateMetaTools(...)`.
66

77
The package now also needs to prove that these tools integrate cleanly with two host-side consumption patterns:
88

@@ -14,7 +14,7 @@ The user explicitly asked for:
1414
- very lightweight host integration
1515
- deterministic scenario-driven tests
1616
- coverage both without embeddings and with embeddings
17-
- a staged auto-discovery flow where the model starts with only two gateway tools, then receives only the currently needed direct tools, and then sees those discovered tools replaced when a new search result arrives
17+
- a staged auto-discovery flow where the model starts with the gateway search, route, and invoke tools, then receives only the currently needed direct tools, and then sees those discovered tools replaced when a new search result arrives
1818

1919
At the same time, the repository still wants to keep the core package generic, library-first, and free from unnecessary host-framework dependencies.
2020

@@ -30,8 +30,8 @@ At the same time, the repository still wants to keep the core package generic, l
3030

3131
The recommended host flow is:
3232

33-
1. expose only `gateway_tools_search` and `gateway_tool_invoke`
34-
2. let the model search the gateway
33+
1. expose only `gateway_tools_search`, `gateway_tools_route`, and `gateway_tool_invoke`
34+
2. let the model search or route the gateway first
3535
3. project only the latest search matches as direct proxy tools
3636
4. replace that discovered proxy set when a new search result arrives
3737

@@ -42,7 +42,7 @@ The core package will not take a hard runtime dependency on Microsoft Agent Fram
4242
```mermaid
4343
flowchart LR
4444
Gateway["IMcpGateway / McpGateway"] --> ToolSet["McpGatewayToolSet"]
45-
ToolSet --> MetaTools["gateway_tools_search + gateway_tool_invoke"]
45+
ToolSet --> MetaTools["gateway_tools_search + gateway_tools_route + gateway_tool_invoke"]
4646
ToolSet --> Discovered["CreateDiscoveredTools(...)"]
4747
MetaTools --> ChatOptions["ChatOptions.AddMcpGatewayTools(...)"]
4848
MetaTools --> AutoDiscovery["McpGatewayAutoDiscoveryChatClient"]
@@ -117,7 +117,7 @@ Mitigations:
117117
- `McpGatewayToolSet.CreateTools(...)` MUST remain the canonical source of gateway meta-tools.
118118
- `McpGatewayToolSet.AddTools(...)` MUST preserve existing tool entries and MUST avoid duplicate names.
119119
- `ChatOptions.AddMcpGatewayTools(...)` MUST preserve existing `ChatOptions.Tools` entries and MUST avoid duplicate names.
120-
- `McpGatewayAutoDiscoveryChatClient` MUST start each host loop with only the two gateway meta-tools visible unless the host already supplied other non-gateway tools.
120+
- `McpGatewayAutoDiscoveryChatClient` MUST start each host loop with only the gateway search, route, and invoke meta-tools visible unless the host already supplied other non-gateway tools.
121121
- `McpGatewayAutoDiscoveryChatClient` MUST replace the discovered proxy-tool set when a newer gateway search result is present instead of accumulating old discovered tools forever.
122122
- The core package MUST stay generic around `AITool` composition and MUST NOT require Microsoft Agent Framework for normal package use.
123123
- Chat-client and agent integration tests MUST prove the staged auto-discovery lifecycle against a realistic multi-tool catalog in both default graph mode and opt-in vector mode.

0 commit comments

Comments
 (0)