Class BowireMcpDualHandlerDispatcher

Namespace
Kuestenlogik.Bowire.Mcp
Assembly
Kuestenlogik.Bowire.Mcp.dll

Dispatches MCP tools/, resources/ and prompts/* traffic to the right backing implementation (full server vs adapter) based on the URL the request hit.

public sealed class BowireMcpDualHandlerDispatcher
Inheritance
BowireMcpDualHandlerDispatcher
Inherited Members

Remarks

The MCP C# SDK installs a global tool/resource/prompt collection populated by WithTools<T> + friends, and merges that collection into every tools/list response — even when a caller installs a WithListToolsHandler overlay (the SDK wraps the overlay and *appends* the static tools to its result). That means the union of static + dynamic tools leaks across endpoints by default.

The dispatcher closes that leak by combining two SDK mechanisms:

  1. WithListToolsHandler / WithCallToolHandler / &c: the adapter's dynamic handlers fire here. For Server requests these return empty results so the SDK's union ends up being just the static tools; for Adapter they return the dynamic ones.
  2. Request filters (post-wrap): a filter runs after the SDK has merged dynamic + static tools and strips out the wrong-side entries based on the URL path. So the adapter URL's tools/list response loses the static tools, and the server URL's response loses any dynamic overlay (none today, but the filter keeps the contract sound). The same gating applies to tools/call: dispatching a server-side tool through the adapter URL throws a clean "tool not found" rather than silently invoking it.

Path routing reads Items[SessionRoutePathItemKey], which Kuestenlogik.Bowire.Mcp.BowireMcpHttpTransportSetup latches at session-init time. Falling back to live Path when Items is empty keeps the path-detection sound in stateless transport mode.

Constructors

BowireMcpDualHandlerDispatcher(BowireMcpEndpointRegistry)

public BowireMcpDualHandlerDispatcher(BowireMcpEndpointRegistry registry)

Parameters

registry BowireMcpEndpointRegistry

Fields

SessionRoutePathItemKey

Key under which the originating request URL is stashed on Items by Kuestenlogik.Bowire.Mcp.BowireMcpHttpTransportSetup.

public const string SessionRoutePathItemKey = "kuestenlogik.bowire.mcp.sessionRoutePath"

Field Value

string

Properties

HasAdapter

True when an adapter has registered itself with the dispatcher. The DI registration uses this flag to decide whether to install any of the With*Handler shims at all — without it, the SDK's own default ListTools/CallTool path (which reads the static ToolCollection) is left in place.

public bool HasAdapter { get; }

Property Value

bool

HasInstalledSdkShims

Marker set by AddBowireMcp after it wires the dispatcher handlers onto the SDK Microsoft.Extensions.DependencyInjection.IMcpServerBuilder. The adapter's AddBowireMcpAdapter consults this so a host that wired the server first doesn't install the same With*Handler shims a second time.

public bool HasInstalledSdkShims { get; set; }

Property Value

bool

Methods

RegisterAdapterHandlers(McpRequestHandler<ListToolsRequestParams, ListToolsResult>, McpRequestHandler<CallToolRequestParams, CallToolResult>, McpRequestHandler<ListResourcesRequestParams, ListResourcesResult>, McpRequestHandler<ReadResourceRequestParams, ReadResourceResult>, McpRequestHandler<ListPromptsRequestParams, ListPromptsResult>, McpRequestHandler<GetPromptRequestParams, GetPromptResult>)

Wire the adapter-side handlers. Called exactly once by the adapter's DI registration; subsequent calls overwrite (so the last AddBowireMcpAdapter wins, which matches the SDK's last-registration-wins convention for global handlers).

public void RegisterAdapterHandlers(McpRequestHandler<ListToolsRequestParams, ListToolsResult> listTools, McpRequestHandler<CallToolRequestParams, CallToolResult> callTool, McpRequestHandler<ListResourcesRequestParams, ListResourcesResult> listResources, McpRequestHandler<ReadResourceRequestParams, ReadResourceResult> readResource, McpRequestHandler<ListPromptsRequestParams, ListPromptsResult> listPrompts, McpRequestHandler<GetPromptRequestParams, GetPromptResult> getPrompt)

Parameters

listTools McpRequestHandler<ListToolsRequestParams, ListToolsResult>
callTool McpRequestHandler<CallToolRequestParams, CallToolResult>
listResources McpRequestHandler<ListResourcesRequestParams, ListResourcesResult>
readResource McpRequestHandler<ReadResourceRequestParams, ReadResourceResult>
listPrompts McpRequestHandler<ListPromptsRequestParams, ListPromptsResult>
getPrompt McpRequestHandler<GetPromptRequestParams, GetPromptResult>