Class BowireForwardingMcpTransport

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

MCP-over-MCP forwarder (#286). Wraps an outbound ModelContextProtocol.Client.McpClient connected to a parent Bowire process's MCP endpoint so an incoming JSON-RPC request handled by this server is marshalled to the parent verbatim and the parent's response is relayed back to the caller.

public sealed class BowireForwardingMcpTransport : IAsyncDisposable
Inheritance
BowireForwardingMcpTransport
Implements
Inherited Members

Remarks

Use case: a remote LLM agent talks to a thin Bowire process (e.g. on a CI runner or in a container) that delegates the actual work to a heavier Bowire running on the operator's workstation. bowire mcp serve --attach <parent-addr> boots the server in forwarder mode — no tools registered locally, every incoming MCP request is forwarded to the parent.

The connection to the parent is established lazily on the first handler call so a child can boot even when the parent isn't reachable yet (e.g. the parent starts a moment later). Failures surface as MCP errors back to the child's caller; the connection is retried on the next handler call.

Lifecycle: DisposeAsync() closes the parent ModelContextProtocol.Client.McpClient + its transport so a graceful child shutdown (SIGTERM, Ctrl+C, host stop) doesn't leak the upstream connection.

Constructors

BowireForwardingMcpTransport(Uri, string?)

Build a forwarder targeting the given parent MCP endpoint URI.

public BowireForwardingMcpTransport(Uri parentEndpoint, string? bearerToken = null)

Parameters

parentEndpoint Uri

HTTP(S) URI of the parent Bowire MCP endpoint (e.g. http://localhost:5198/bowire/mcp).

bearerToken string

Optional bearer token to attach to every request to the parent (Authorization: Bearer <secret>). Required when the parent was started with --token <secret>; ignored when the parent has no token configured.

Properties

HasBearerToken

Whether a bearer token was supplied at construction. Exposed for diagnostics — doesn't surface the token itself.

public bool HasBearerToken { get; }

Property Value

bool

ParentEndpoint

The endpoint this forwarder was configured against. Exposed for diagnostics (the CLI banner, tests).

public Uri ParentEndpoint { get; }

Property Value

Uri

Methods

DisposeAsync()

public ValueTask DisposeAsync()

Returns

ValueTask

GetClientAsync(CancellationToken)

Lazily establish + return the underlying MCP client. Repeated callers share the same client; if a previous call left the client in a faulted state the caller can call ResetAsync() before retrying. Thread-safe via an init semaphore.

public Task<McpClient> GetClientAsync(CancellationToken ct)

Parameters

ct CancellationToken

Returns

Task<McpClient>

ResetAsync()

Tear down the current client + transport so the next GetClientAsync(CancellationToken) call rebuilds them. Useful after a connection error so a transient parent restart doesn't sink the child for its remaining lifetime.

public Task ResetAsync()

Returns

Task

TryParseAttachEndpoint(string?, out Uri?, out string)

Parse the documented --attach argument shapes into a concrete parent MCP endpoint URI. Accepts:

  • host:port — expanded to http://host:port/bowire/mcp (the path Bowire's HTTP-bind serves MCP at).
  • An absolute http(s) URI — used as-is.

Returns false + a human-readable reason for malformed input; the caller surfaces the message to the operator.

public static bool TryParseAttachEndpoint(string? raw, out Uri? endpoint, out string error)

Parameters

raw string
endpoint Uri
error string

Returns

bool