9.1 KiB
MCP Protocol Relay Design
Goal
Make wf-mcp a truthful high-quality MCP proxy, not merely a tool forwarder.
The next phase is about understanding and then projecting advanced MCP protocol behavior correctly:
- upstream notifications
- server capability advertisement
- downstream client capability relay
- later request forwarding for client-side features such as elicitation, roots, sampling, and task-augmented requests
The immediate next work is still investigation first, because advertising a
capability before we can bridge it end to end would make wf-mcp lie to clients.
Current State
wf-mcp already has:
- a unified MCP surface
- proxy mounting with stable public names
- local list-changed notifications after reload
- ordinary tool/resource/prompt proxying
- resource-link URI rewriting for ordinary tool-returned
ResourceLinks
Known remaining uncertainty:
- which advanced MCP requests FastMCP can already bridge
- how initialization capabilities should be projected through a broker with multiple enabled upstream connections
Resolved after this spec was written:
- generic upstream notifications such as list-changed, resource-updated, and logging notifications do not automatically reach downstream clients through the current proxy path
- current capability negotiation is not the reason for that gap
See ../../mcp_protocol_proxy_inventory.md
for the measured facts and
../../wf_mcp_proxy_reality_and_roadmap.md
for the current practical roadmap.
Core Distinction
MCP has two different capability directions.
Server capabilities
These are advertised by wf-mcp to its downstream client:
toolsresourcespromptsloggingcompletionstasks
wf-mcp may advertise a server capability only when the unified server can
actually serve it. The correct eventual shape is a selected union:
- local capabilities implemented by
wf-mcp - plus enabled upstream server capabilities that
wf-mcpcan proxy safely
Client capabilities
These are advertised by the downstream client to wf-mcp:
rootssamplingelicitation- client-side
tasks
wf-mcp must not advertise these as its own server capabilities. Instead, when
an upstream server needs one of them, wf-mcp may relay the downstream client's
real capabilities upstream only if the corresponding request path can also be
forwarded correctly.
Examples:
- If the downstream client supports elicitation and
wf-mcpcan relayelicitation/create, an upstream server may use elicitation through the proxy. - If the downstream client supports roots and
wf-mcpcan relayroots/list, an upstream server may request roots through the proxy.
Design Principles
-
Truthful advertisement Never advertise a capability solely because some upstream declares it.
-
Direction-aware relay Keep server-side projection and client-side relay as separate concepts.
-
Selected union, not blind union Disabled upstreams and unsupported bridge paths must not inflate the public capability set.
-
Protocol first, workflow later This phase improves MCP proxy behavior. Workflow artifacts and planner-facing source models should not absorb this complexity.
-
Use official MCP boundary types Model protocol behavior with
mcp.typescapability and notification models where possible. Do not create parallel ad hoc protocol dicts. -
Consent at source connection time A connected proxy source should normally be able to use the capabilities it declares, but powerful client-side relays should be visible to the user when the source is connected, similar to an app-permissions screen. Later admin UI may disable individual relays such as sampling, roots, or elicitation for a source without changing the base protocol model.
Phase 1: Upstream Notification Inventory
Questions
- Does FastMCP already forward upstream:
notifications/tools/list_changednotifications/resources/list_changednotifications/prompts/list_changednotifications/resources/updatednotifications/messagenotifications/progressnotifications/tasks/status
- If some notifications are forwarded, are payloads transformed correctly?
- For resource updates, are resource URIs projected into downstream namespace form or leaked as raw upstream URIs?
- Do local client surfaces we can inspect, such as Inspector or Codex, visibly react to the forwarded notifications?
Method
- Extend the fixture server only where needed to emit deterministic events.
- Probe direct upstream behavior first.
- Probe the same behavior through
wf-mcp. - Record observed behavior in
docs/mcp_protocol_proxy_inventory.md. - Do not implement forwarding until the behavior gap is proven.
Expected Outputs
- a table of each notification kind:
- direct upstream behavior
- through-proxy behavior
- payload correctness
- whether action is needed
- regression tests for any behavior we decide to rely on
Phase 2: Capability Negotiation Inventory
Questions
- What server capabilities does the current unified
wf-mcpserver advertise? - What server capabilities do configured upstreams advertise?
- What downstream client capabilities are visible to
wf-mcpduring initialization? - Which advanced features can FastMCP already bridge today?
- Which advertised upstream capabilities would be false for
wf-mcpto expose until request forwarding is added?
Expected Outputs
- a server-capability projection matrix
- a client-capability relay matrix
- a concrete list of:
- already safe to advertise
- bridgeable with small glue
- unsupported until a larger forwarding subsystem exists
Intended Architecture
Likely later modules:
wf_mcp.protocol_relay/
server_capabilities.py
client_capabilities.py
notifications.py
Conceptually:
enabled upstream capabilities
+
local wf-mcp capabilities
|
v
ServerCapabilityProjection
|
v
downstream initialize result
downstream client capabilities
|
v
ClientCapabilityRelay
|
v
selected upstream initialize requests
The exact module names may change after the inventory. The separation should not:
- projecting what
wf-mcpoffers as a server - relaying what the downstream client offers to upstream servers
Mixed upstream support should not be hidden. MCP's top-level capability object is
coarse, so wf-mcp should eventually expose per-source and, where useful,
per-capability detail through its admin inventory tools even when the public
server capability advertisement is a selected union.
Feature Notes
Elicitation
Elicitation is a client capability, not a server capability. Supporting it
through wf-mcp means forwarding upstream elicitation requests to the
downstream client and returning the response.
Roots
Roots are also a client capability. They represent client-exposed filesystem or
workspace roots that a server may query. Supporting roots through wf-mcp means
relaying roots/list, not pretending wf-mcp itself owns roots.
Sampling
Sampling is another client capability. It lets a server ask the client-side LLM to generate content. This is powerful and should remain explicit because it can cross trust boundaries.
Tasks
Tasks exist in both directions:
- server tasks: downstream client task-augments calls into
wf-mcp - client tasks: upstream servers task-augment requests they send toward the downstream client
Treat these as separate negotiated capabilities. Do not assume “tasks supported” is one global boolean.
MCP Apps And _meta.ui
_meta.ui belongs to the MCP Apps extension layer, not the base server/client
capability split above. wf-mcp should still preserve and proxy it faithfully.
Expected proxy behavior:
- preserve upstream
_meta.uivalues - rewrite referenced UI resource URIs when they cross a namespace boundary, the same way ordinary proxied resource URIs are projected
- do not rely on
_meta.uifor core protocol correctness, because clients may ignore extension metadata they do not understand
Possible later use:
wf-mcpmay expose its own source-consent, login/logout, catalog, and toggle surfaces as MCP Apps UI resources/tools- that is a UI delivery choice, not a reason to mix UI metadata into the core capability negotiation model
Non-Goals For The Next Pass
- Do not blindly union all upstream capability dicts.
- Do not build a custom protocol stack around FastMCP before measuring what it already forwards.
- Do not solve workflow-artifact dependency validation here.
- Do not build UI controls here.
- Do not claim support for elicitation, roots, sampling, or tasks until an end-to-end relay path is demonstrated.
Success Criteria
The next pass is successful when we can answer, with evidence:
- Which upstream notifications already survive the proxy?
- Which server capabilities may
wf-mcpadvertise truthfully today? - Which downstream client capabilities can safely be relayed upstream today?
- What exact small implementation should happen next, instead of guessing?