subclass Transform js to get our shit looking nice holy moly

This commit is contained in:
lda
2026-05-15 23:37:45 +07:00 Verified
parent 6935329d44
commit 9a035e9557
8 changed files with 224 additions and 24 deletions
+7 -10
View File
@@ -98,12 +98,11 @@ Tool simulate-research-query requires task augmentation (taskSupport: 'required'
- Proxy tool projection: healthy for ordinary tools.
- Proxy resource list/read projection: healthy for listed resources and
templates.
- Tool-result resource links: incomplete; embedded resource URIs need explicit
rewrite or a documented limitation.
- `wf_mcp.proxy_results` now contains pure typed helpers for rewriting
`mcp.types.ResourceLink` content inside `mcp.types.CallToolResult`. These
helpers are not wired into the FastMCP proxy runtime yet because FastMCP does
not currently expose a result-transform hook.
- Tool-result resource links: ordinary embedded resource URIs are now rewritten
by a local proxy transform so downstream clients receive namespaced URIs.
- `wf_mcp.proxy_results` contains pure typed helpers for rewriting
`mcp.types.ResourceLink` content and a small FastMCP workaround transform that
wraps proxied tools until upstream FastMCP handles this projection itself.
- Session resources: unresolved; needs a focused test because session affinity
may matter.
- Tasks: unsupported; task-required tools should remain clearly diagnosed until
@@ -115,10 +114,8 @@ Tool simulate-research-query requires task augmentation (taskSupport: 'required'
- Add automated tests for listed resource/template namespacing through unified
mode.
- Add a fixture or Everything integration test showing tool-returned
`resource_link` URIs are not rewritten today.
- Decide whether to rewrite embedded resource links in tool results or expose a
helper that resolves upstream resource links into proxied resource URIs.
- Keep the fixture regression proving ordinary tool-returned `resource_link`
URIs are rewritten into downstream-facing namespaced URIs.
- Investigate session resource lifecycle separately from URI rewriting.
- Inventory current FastMCP/MCP SDK APIs for tasks, logging notifications, and
resource update notifications before implementing protocol forwarding.
@@ -178,10 +178,28 @@ actually implemented.
- wiring `wf_mcp.proxy_results` helpers into real tool-result handling
- replacing FastMCP behavior that should be fixed upstream instead
## Open Question
## Investigation Result
Whether reused mounts should keep FastMCP `ProxyProvider` component caches across
reload is not yet a promise. Reuse likely does preserve those caches. If that
causes stale catalog behavior, the registry should either invalidate the provider
cache explicitly when supported or treat reload as a reason to rebuild mounts
for affected connections.
FastMCP 3.3.0 makes unchanged-mount reuse reasonable, with caveats:
- `create_proxy_mount()` passes a disconnected `Client` into `create_proxy(...)`.
FastMCP turns that into `client.new()` per request, so reusing a
`FastMCPProxy` does **not** keep one permanently connected upstream session
alive.
- `ProxyProvider` intentionally caches tools, resources, templates, and prompts
for lookup efficiency. The default TTL is 300 seconds.
- Every explicit `list_*` call refreshes the corresponding proxy cache. Direct
lookup paths may use a still-fresh cached list until TTL expiry.
- `FastMCP.mount()` returns no provider handle, and no public general-purpose
unmount API was found. Parent-side provider-list rebuild is still the only
safe visible-surface removal we currently own.
Therefore the current registry behavior is acceptable:
- reuse unchanged enabled mounts
- rebuild changed mounts
- stop remounting disabled or removed mounts
- do not claim safe close/unmount of retired mounts yet
If dynamic upstream catalogs become a practical problem, prefer a documented
refresh/invalidation policy over throwing away unchanged mounts on every reload.
+18 -5
View File
@@ -64,6 +64,20 @@ Unified mode currently reuses `ProxyRuntime` as its proxy mounting engine. The
the place where configured upstream MCP connections become mounted FastMCP
providers. `TransparentProxyRuntime` remains a compatibility alias.
`ProxyRuntime` now owns a small `ProxyMountRegistry`. Reload still clears the
visible mounted provider list and rebuilds it from current config, but unchanged
enabled connections reuse their cached proxy mount instead of recreating a new
client/proxy pair every time. Disabled or removed connections are no longer
mounted after reload, but their cached mounts are only *retired* internally for
now; they are not safely closed or unmounted because FastMCP does not yet expose
the lifecycle hook we need.
The reused FastMCP proxies are not holding one forever-open upstream connection.
`create_proxy_mount()` gives `create_proxy(...)` a disconnected client, and
FastMCP creates fresh request clients from it. What persists across reload is the
proxy/provider object and its component-list caches. FastMCP refreshes those
caches on explicit `list_*` calls and otherwise expires them after its TTL.
After a successful reload, the runtime publishes local `tools_changed`,
`resources_changed`, `prompts_changed`, and `catalog_changed` events when an
event bus is supplied. The admin MCP tool projects the same event kinds into
@@ -75,11 +89,10 @@ typed result to a plain payload at the boundary.
Proxy tool listing similarly uses `ProxyToolPayload` / `ProxyToolsPage`
internally and serializes to admin MCP payloads at the boundary.
Do not memoize mounted proxies or clients without an explicit lifecycle design.
The tempting implementation is a dictionary keyed by connection id around
`create_proxy(client, ...)`, but cached clients need clear close/reconnect/error
semantics. Prefer FastMCP's official unmount/provider lifecycle when it becomes
available.
Do not add more lifecycle behavior outside `ProxyMountRegistry`. Cached clients
still need clear close/reconnect/error semantics, and the registry is the single
place where that future behavior should land. Prefer FastMCP's official
unmount/provider lifecycle when it becomes available.
Do not add notification proxying or long-lived subscription handling across
reloads without first introducing an explicit mount lifecycle boundary.