From 0f90bb682a9c8460571c1a664fe83e85be276cbc Mon Sep 17 00:00:00 2001 From: TJ Hoplock Date: Sun, 14 Sep 2025 22:32:27 -0400 Subject: [PATCH] feat: extend resource middlewares to resource templates The existing logic for resource middlewares was based heavily off of the prior art for tool middlewares. Tools, however, don't have the concept of templates. This change ensures that resource middlewares also get applied to resource templates. While they are defined as separate types, both `ResourceHandlerFunc` and `ResourceTemplateHandlerFunc` have the same function signature, and therefore satisfy the same underlying type. This seems intentional since resource templates are only actually executed within the same `handleReadResource` function, and only when A) no direct resources match and B) the resource uri matches the template -- executing in the same function and returning an `mcp.ReadResourceResult` regardless of direct resource or template. Since the uri needs to match in order for the template to be executed, we build the chain of middleware funcs after matching the uri to avoid wasted cycles. In order to apply the middlewares to the `ResourceTemplateHandlerFunc`, we explicitly cast to a `ResourceHandlerFunc`. This is safe, as they point to the same underlying type. If resource handler funcs ever diverge, this should also safely surface as a compile time warning, allowing ample time to address and not have a silent breakage slip through in builds. Signed-off-by: TJ Hoplock --- server/server.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/server.go b/server/server.go index b9fb3612..f602f74d 100644 --- a/server/server.go +++ b/server/server.go @@ -940,7 +940,17 @@ func (s *MCPServer) handleReadResource( s.resourcesMu.RUnlock() if matched { - contents, err := matchedHandler(ctx, request) + // If a match is found, then we have a final handler and can + // apply middlewares. + s.resourceMiddlewareMu.RLock() + finalHandler := ResourceHandlerFunc(matchedHandler) + mw := s.resourceHandlerMiddlewares + // Apply middlewares in reverse order + for i := len(mw) - 1; i >= 0; i-- { + finalHandler = mw[i](finalHandler) + } + s.resourceMiddlewareMu.RUnlock() + contents, err := finalHandler(ctx, request) if err != nil { return nil, &requestError{ id: id,