Repository navigation
Conversation
This comment has been minimized.
This comment has been minimized.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
BrickerP
left a comment
There was a problem hiding this comment.
Review
Approve. This is a clean alignment of the three list middleware paths with the typed contract the hooks already declare.
What was wrong
on_list_tools / on_list_resources / on_list_resource_templates / on_list_prompts are typed as MiddlewareContext[List*Request], and list_tools already builds mcp_types.ListToolsRequest(method="tools/list"). The other three list methods passed message={}, so middleware that reads context.message.method (or anything else on the request model) works for tools/list and raises AttributeError on resources/list, resources/templates/list, and prompts/list. Same drift shows up in StructuredLoggingMiddleware(include_payloads=True) as empty {} payloads with payload_type dict.
Fix
The three sites in server.py now construct the matching mcp_types.ListResourcesRequest / ListResourceTemplatesRequest / ListPromptsRequest with the same method strings already used on MiddlewareContext.method. That mirrors the tools path at ~L835 and matches the declared hook types — no new abstraction, no behavior change beyond the message shape middleware sees.
Tests
test_list_hooks_receive_typed_request_messages exercises all four list client calls and asserts both isinstance(..., List*Request) and message.method for each hook. That would have failed on main with the dict payload and covers the regression cleanly.
Notes
- Issue #5710 is unassigned; this PR is the only open fix referencing it.
- CI looks green across the Python matrix / integration / MCP conformance / static analysis (issue-link check skipped as expected for this labeling).
- No concerns about constructor kwargs: these request models are used elsewhere the same way, and only
methodis required for the middleware surface here.
LGTM.
|
Checked this against main (e2fcc41) and the fix looks right. The new test fails on main (the hook receives a dict) and passes here, tests/server/middleware is green, and the typed models now also reach on_message and on_request for these three methods, not just the typed list hooks. That is a nice side effect, since those hooks are annotated with mt.Request. One visible change worth knowing: with StructuredLoggingMiddleware(include_payloads=True) the list payload goes from One limitation I think is worth a note, though not a blocker for this PR: the message is built with only |
7385941 to
4ec278f
Compare
|
Thanks for the thorough check — the limitation you flagged is real, and I took the first option: each of the three construction sites now carries a short comment (new commit 4ec278f, branch also rebased onto the current main) noting that the message is synthesized with only Your pagination repro matches the code path: Validation on the new head: |
on_list_resources, on_list_resource_templates, and on_list_prompts received a bare empty dict as context.message, unlike on_list_tools which receives mt.ListToolsRequest. Pass the declared request models so middleware written against the typed hook signatures can read message.method on every list method.
The synthesized ListXRequest messages are built with only `method`, so `params` stays None and pagination state (e.g. the client's nextCursor) is not visible to list middleware hooks. Add a short comment at each construction site so the typed hook signature does not imply the cursor is available. Docs-only; no behavior change.
4ec278f to
0b6fc72
Compare
Description
Closes #5710
Summary
The four typed list hooks on
Middlewaredeclarecontext.messageas a request model:on_list_tools/on_list_resources/on_list_resource_templates/on_list_promptsare typedMiddlewareContext[mt.ListToolsRequest]/[mt.ListResourcesRequest]/[mt.ListResourceTemplatesRequest]/[mt.ListPromptsRequest](fastmcp_slim/fastmcp/server/middleware/middleware.py:262-285). At runtimelist_toolsreceivesListToolsRequest(method="tools/list"), but the other three list methods dispatched middleware withmessage={}, so a middleware written against the declared types can readcontext.message.methodontools/listyet hitsAttributeErroronresources/list,resources/templates/list, andprompts/list.Why
list_toolsbuilds the typed model atfastmcp_slim/fastmcp/server/server.py:835, butlist_resources,list_resource_templates, andlist_promptspassed a bare{}as the middleware message atserver.py:976,:1111, and:1245.Changes
fastmcp_slim/fastmcp/server/server.py: constructmcp_types.ListResourcesRequest(method="resources/list"),mcp_types.ListResourceTemplatesRequest(method="resources/templates/list"), andmcp_types.ListPromptsRequest(method="prompts/list")for the three middleware contexts, mirroring the tools path.tests/server/middleware/test_middleware.py: addtest_list_hooks_receive_typed_request_messages, asserting each of the four list hooks receives its declared request model with the correspondingmethod.Testing
AssertionError: on_list_resources received dict, expected ListResourcesRequest(1 failed).uv run pytest "tests/server/middleware/test_middleware.py::TestMiddlewareHooks::test_list_hooks_receive_typed_request_messages"→ 1 passed.uv run pytest tests/server/middleware/→ 325 passed.uv run pytest tests/server/→ 9 failed, 4308 passed, 2 skipped; the 9 failures are pre-existing proxy/SSE/mount connection tests that fail identically onmainwithout this change.uv run prek run --all-files→ all checks pass (ruff check, ruff format, ty, prettier, gitleaks, codespell).Contribution type
Checklist
uv run prek run --all-filesand all checks pass