Skip to content

Conversation

tjhop
Copy link
Contributor

@tjhop tjhop commented Sep 15, 2025

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 t.hoplock@gmail.com

Summary by CodeRabbit

  • Bug Fixes
    • Resource middlewares are now applied to template-based resource routes, matching the behavior of direct resources.
    • Ensures consistent enforcement of authentication, headers, rate limits, logging, and other policies across all resources.
    • Resolves cases where middleware-driven policies or transformations were previously skipped on templated routes.
    • Improves reliability and observability without changing response content beyond middleware effects.
    • No configuration changes required.

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 <t.hoplock@gmail.com>
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds resource middleware execution to template-matched resource reads in server/server.go. When a URI matches a resource template, the matched handler is converted to a ResourceHandlerFunc, wrapped with resource middlewares (applied in reverse order) under resourceMiddlewareMu, and then invoked. Direct-resource handling was already using this path.

Changes

Cohort / File(s) Summary
Resource read path handling
server/server.go
handleReadResource now wraps template-matched resource handlers with the resource middleware chain (reverse order) using resourceMiddlewareMu before invoking. Direct resource handlers were already wrapped; this change aligns template-based handling. No public APIs changed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description contains a clear narrative and rationale for the change but does not follow the repository's required template: it is missing the "## Description" header with a Fixes # reference (if applicable), the "## Type of Change" checklist, the "## Checklist" section, and the "## MCP Spec Compliance" block, so it does not meet the repository's structured template requirements. Please update the PR description to match the repository template by adding the "## Description" header (and Fixes # if applicable), completing the "## Type of Change" checkboxes and the "## Checklist" entries, and including or explicitly omitting the "## MCP Spec Compliance" section as appropriate; also mention tests and documentation updates so reviewers can verify completeness.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: extend resource middlewares to resource templates" is concise, follows conventional-commit style, and directly summarizes the primary change in the PR (applying resource middlewares to template-matched resources), making the intent clear to reviewers scanning history. It contains no extraneous detail or vague phrasing and accurately reflects the changes described in the summary and objectives.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
server/server.go (2)

943-953: Minimize lock hold and DRY the wrapping logic.

Copy the middleware slice under the read lock, release it, then build the chain; also consider a helper to avoid duplicating the wrapping in both code paths.

Apply this diff within this block:

-        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()
+        // Copy middlewares under lock, then build chain outside the lock
+        s.resourceMiddlewareMu.RLock()
+        mw := append([]ResourceHandlerMiddleware(nil), s.resourceHandlerMiddlewares...)
+        s.resourceMiddlewareMu.RUnlock()
+        finalHandler := ResourceHandlerFunc(matchedHandler)
+        for i := len(mw) - 1; i >= 0; i-- {
+            finalHandler = mw[i](finalHandler)
+        }

Optionally, introduce a helper and call it here to remove duplication:

// place near other server methods
func (s *MCPServer) wrapResourceHandler(h ResourceHandlerFunc) ResourceHandlerFunc {
    s.resourceMiddlewareMu.RLock()
    mw := append([]ResourceHandlerMiddleware(nil), s.resourceHandlerMiddlewares...)
    s.resourceMiddlewareMu.RUnlock()
    for i := len(mw)-1; i>=0; i-- {
        h = mw[i](h)
    }
    return h
}

Then this block becomes:

finalHandler := s.wrapResourceHandler(ResourceHandlerFunc(matchedHandler))

And in the direct-resource path, replace the equivalent wrapping with:

finalHandler := s.wrapResourceHandler(handler)

943-953: Add a unit test to assert template handlers pass through middlewares.

Test that a template-matched read triggers middlewares (order is reverse-registration) and that WithResourceRecovery recovers panics from a template handler.

I can draft a focused test that registers a template and a counter/panic middleware to verify execution and order. Want me to open one?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 840879b and 0f90bb6.

📒 Files selected for processing (1)
  • server/server.go (1 hunks)
🔇 Additional comments (1)
server/server.go (1)

943-953: Middleware now applied to template handlers — parity achieved.

Good call converting the matched template handler and running it through the same middleware chain as direct resources. This closes a real behavior gap.

@ezynda3 ezynda3 added the type: enhancement New feature or enhancement request label Sep 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or enhancement request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants