Repository navigation
test(darling): live HTTP proof of the MCP host's gates, including /core (#4128b) - #4142
Merged
Merged
Conversation
…, and check /core's instructions note in the initialize result, where the instructions travel
erikdarlingdata
marked this pull request as ready for review
September 24, 2026 06:13
erikdarlingdata
enabled auto-merge (squash)
September 24, 2026 06:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4128. Part (a), the web host, merged in #4132; this PR does part (b), the MCP host, and finishes the issue.
Part (b) of #4128: a live-HTTP proof of the MCP host's gates, including
/core, modeled on merged PR #4132 (the web host's part (a)).What changed
DarlingMcpHostService.TryStartServerAsynchad two seams extracted, so a test builds the SAME server production wires instead of a hand-copied one that could silently drift:internal static void ConfigureMcpServices(IServiceCollection services, DarlingPeerDirectory.Snapshot declaredPeers). Everything frombuilder.Services.AddMcpServer(...)through the.WithRequestFilters(...)call (every tool class registration, the stateless transport,/core'sConfigureSessionOptionsnarrowing) moved in, unchanged except the receiver (builder.Servicesthere vs. theservicesparameter here).internal void ConfigurePipeline(WebApplication app, bool networkMode, IPAddress? networkListenIp, IPNetwork allowedCidr, string bearerToken). Everything afterbuilder.Build()throughMapMcp("/core")moved in: the Host-header/DNS-rebinding guard (both modes), the network-mode bearer-token + CIDR middleware, and bothMapMcp()calls. Renamed_app.Use/_app.MapMcptoapp.Use/app.MapMcpinside the method, mirroring the rename test(darling): live HTTP proof of the web host's gates (#4128a) #4132 made for the web host'sConfigurePipeline.Production (
TryStartServerAsync) calls both in the same order with the same values it always built inline; every moved line is textually identical to before the extraction.Darling/Darling.Tests/DarlingMcpHostGateLiveTests.csis new: builds aTestServerrunning the realConfigureMcpServices+ConfigurePipeline, with a deadNpgsqlDataSource(never opened — every case here is refused, or reachestools/list, before any tool body runs) and a null plan fetcher. Cases:/and/coretools/liston/succeeds (200)tools/liston/succeeds (no token middleware installed at all in this mode)/coretools/list: exactlyDarlingCoreToolProfile.Closure's pinned tool-name set, with the instructions containing the/corenotetools/callon/corefor a tool outside that closure (delete_custom_view): the SDK's own "Unknown tool" error, proving/coreis a real subset (no fallbackCallToolHandleris registered), not a listing filterMCP is spoken as plain JSON-RPC HTTP POSTs over
TestServer.SendAsync(notCreateClient, since the CIDR gate readscontext.Connection.RemoteIpAddress, whichCreateClient's in-memory context leaves null) with a caller-owned responseMemoryStream(TestServer's own response body stream doesn't supportSeek).HostHeaderGuardTests updates
ReadHostSourceanchor comment and every_app.MapMcp(...)literal pin forDarlingMcpHostService.csare updated toapp.MapMcp(...), matching the parameter rename.McpHost_HasNoPathConditionalMiddlewareBeforeTheMcpMappingsis rescoped toConfigurePipeline's own method body (found byinternal void ConfigurePipeline(first) rather than a whole-file text slice frombuilder.Build()toMapMcp("/core"). Reason:ConfigureMcpServices— now textually BETWEENTryStartServerAsync'sbuilder.Build()andConfigurePipeline'sMapMcp("/core")— contains its own unrelatedcontext.Request.Pathcheck insideConfigureSessionOptions(the/coretool-set narrowing), which a whole-file slice would wrongly flag as a path-conditional gate that could skip/core. Scoping toConfigurePipeline's body is the accurate claim: that method is the real, in-order request pipeline.EachHost_RunsTheGuardInBothModes_NotOnlyWhenNetworkExposedalready searched from the guard's own position (an existing test(darling): live HTTP proof of the web host's gates (#4128a) #4132-era accommodation) and needed no further change for the MCP host.What I ran
dotnet buildonPerformanceMonitor.Darling.Service.csprojandDarling.Tests.csprojwith-p:EnableWindowsTargeting=true: both succeed, 0 warnings on the service project, 0 errors on Tests (after fixing aHashSet<string?>nullability warning).dotnet Darling.Tests.dll -class Darling.Tests.DarlingMcpHostGateLiveTestson the Mac test rig (Windows-target stripped from the runtimeconfig): reached 10 test cases; iterated on failures (the CIDR gate needs an explicitRemoteIpAddresssinceCreateClient's context leaves it null;TestServer's response body stream doesn't supportSeek) and was mid-fix on theSeekissue (switching to a caller-ownedMemoryStream) when I hit the context wall.What I did NOT verify
MemoryStreaminstead ofctx.Response.Body.Seek) is committed and builds clean, but I have not re-runDarlingMcpHostGateLiveTeststo confirm all 10 cases pass with that fix.DarlingMcpHostService, and the FULL Darling suite — not run at all. The brief's step 5 (run the new class,HostHeaderGuardTests, every class touchingDarlingMcpHostService, then the full suite once withDARLING_TEST_PGunset) is undone.Next step for whoever picks this up: run
DarlingMcpHostGateLiveTestsandHostHeaderGuardTestsfirst; if theSeekfix holds, run the full suite once and diff failures against dev's known 214 Windows-only baseline before readying this PR.Coordinator verification (branch head 7ca8bb7)
TryStartServerAsyncreappears inConfigureMcpServicesorConfigurePipeline, identical except the receiver (builder.Services→services,_app→app). The only other additions are the two signatures, their doc comments and the two call sites./core's instructions travel in theinitializeresult, nottools/list. The case now initializes on both paths and checks that only/coreleads withDarlingCoreToolProfile.CoreNote;TestServer.CreateClientis now prose (DocCommentHygieneTests).DarlingMcpHostGateLiveTestspasses (10/10),HostHeaderGuardTestspasses (46/46), andDocCommentHygieneTestspasses (77/77). The full Darling suite on macOS equals dev's baseline once the cref fix is in: the only extra failures in the full run were those two DocCommentHygiene tests.CHANGELOG entry
/core's narrowed tool set — by extracting the host's service registration and pipeline wiring into two reusable methods (ConfigureMcpServices,ConfigurePipeline) that a test now drives directly.Part of #4128.