docs: add a middleware tutorial that refuses tool calls

Merged
MA
1mo ago
+266-6

Fixes #3272.

The Middleware page lists "Refuse" among the things a middleware can do, but the only code for it was on the Subscriptions page. This adds a worked example to the Middleware page itself: a middleware that caps how many tool calls run at once and refuses the rest.

It covers the gap #3272 pointed out with a small deterministic example in the middleware docs, rather than the separate model-backed example server the issue proposed.

What it adds

  • docs_src/middleware/tutorial002.py
    • max_concurrent_tool_calls(limit), a factory that returns the middleware, on the low-level Server like the page's first example.
    • Only tools/call is counted, so discovery and listing keep answering while tool calls are refused.
    • Over the limit it raises MCPError with the server's own SERVER_BUSY code. MCP has no standard code for "busy", and the spec leaves codes outside the JSON-RPC reserved range to applications.
  • docs/advanced/middleware.md
    • A new "A concurrency cap" section after the timing example.
    • The "Refuse" bullet now points at it, and still points at the subscriptions example.
  • tests/docs_src/test_middleware.py
    • The call over the limit is refused while the earlier ones are still running, including when it comes from a second client.
    • tools/list is answered with the cap reached.
    • A call that finishes, raises or is cancelled gives its slot back.
    • Answering with an is_error=True result instead of raising reaches the client as a tool result.

Notes

  • Nothing changes under src/ and there is no new API.
  • The translated pages aren't regenerated here. They show the "behind the English page" notice until the next translation refresh, the same as after any other change to an English page.

AI Disclaimer