Skip to content

Fix ty ignore syntax in OpenAPI provider#3253

Merged
jlowin merged 4 commits intomainfrom
fix/ty-ignore-syntax
Feb 20, 2026
Merged

Fix ty ignore syntax in OpenAPI provider#3253
jlowin merged 4 commits intomainfrom
fix/ty-ignore-syntax

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Feb 20, 2026

The type: ignore[arg-type] comment on SchemaPath.from_dict() uses mypy syntax, which ty doesn't recognize as a targeted suppression. Newer ty flags it as an unused blanket ignore, breaking the upgrade checks workflow (#3214). Replaced with ty: ignore[invalid-argument-type].

Also fixes two flaky tests that only fail on Python 3.10 / ubuntu CI runners. The rate limiting test was counting exact MCP protocol messages to predict burst exhaustion, but the SDK sends a variable number of internal messages (and the refill rate was high enough that tokens leaked back in). Now uses near-zero refill with a loop-until-limited approach. The ping interval test had a 150ms window for 50ms-interval pings — too tight for slow runners — widened to 350ms.

Closes #3214

@marvin-context-protocol marvin-context-protocol Bot added bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. openapi Related to OpenAPI integration, parsing, or code generation features. labels Feb 20, 2026
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: A pre-existing flaky test unrelated to this PR failed on Python 3.10/ubuntu. This PR's single-line change (replacing a type: ignore comment) cannot cause this failure.

Root Cause: test_rate_limiting_blocks_rapid_requests is timing-sensitive. It sets max_requests_per_second=10.0 with burst_capacity=6, then makes 3 call_tool calls and expects the 4th to be rate-limited. The token bucket refills at 10 tokens/second — so as little as 0.1 seconds of elapsed time between async calls replenishes a token, allowing the 4th call to succeed instead of being blocked. On Python 3.10/ubuntu, async scheduling introduces enough real-world latency that the bucket refills before the limit is hit. Python 3.13 and Windows pass because their async scheduling is faster in this context.

Suggested Solution: The test in tests/server/middleware/test_rate_limiting.py:307 needs to be made timing-independent. Options:

  1. Reduce the refill rate significantly — use max_requests_per_second=0.01 (or similarly tiny value) so tokens cannot realistically replenish between test calls.
  2. Mock time.time() in the rate limiter so the test controls elapsed time explicitly, making it fully deterministic.
  3. Consume all tokens upfront — use a burst capacity equal to exactly the number of calls made, with a refill rate so low it cannot recover within the test's duration.

The cleanest fix is option 1 or 2. Option 1 is a one-line change to the test fixture parameters.

Detailed Analysis

Failing test:

FAILED tests/server/middleware/test_rate_limiting.py::TestRateLimitingMiddlewareIntegration::test_rate_limiting_blocks_rapid_requests
Failed: DID NOT RAISE <class 'fastmcp.exceptions.ToolError'>

Token bucket math showing the race condition:

With max_requests_per_second=10.0, burst_capacity=6:

  • Tokens start at 6
  • Each request consumes 1 token
  • Tokens refill at 10/second

Expected sequence: initialize(−1) + list_tools(−1) + call_tool×3(−3) = 1 token left → 4th call should fail.

But TokenBucketRateLimiter.consume() (in src/fastmcp/server/middleware/rate_limiting.py:48-57) adds elapsed * refill_rate tokens on every call. Even 0.1 seconds of real elapsed time between the 3rd and 4th call_tool adds 1.0 token back, bringing the bucket back to 2 — allowing the 4th call through.

Why only Python 3.10/ubuntu?
Python 3.13 has improved async task scheduling that reduces per-call overhead. Windows has different timer resolution. Python 3.10 on ubuntu tends to have higher per-coroutine overhead, making the elapsed time between calls measurably larger.

This PR is unrelated — it only changes one line in src/fastmcp/server/providers/openapi/provider.py (a type annotation comment: type: ignore[arg-type]ty: ignore[invalid-argument-type]).

Related Files
  • tests/server/middleware/test_rate_limiting.py:307 — the flaky test; needs timing-independent parameters
  • src/fastmcp/server/middleware/rate_limiting.py:38-58TokenBucketRateLimiter.consume(), the method that refills tokens based on real elapsed time

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccb52f07f7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +326 to +327
except (ToolError, McpError):
hit_limit = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify exception indicates rate limiting

This loop treats any ToolError or McpError as success, so the test can pass even when rate limiting is not what failed first (for example, a transport/JSON-RPC error or another tool-call failure). That weakens the integration check and can hide regressions in the limiter itself, because the assertion only checks that some exception occurred. Narrow the assertion to rate-limit-specific error details (message/code) so the test only passes when throttling is actually triggered.

Useful? React with 👍 / 👎.

@jlowin jlowin merged commit 1d7e92e into main Feb 20, 2026
7 checks passed
@jlowin jlowin deleted the fix/ty-ignore-syntax branch February 20, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. openapi Related to OpenAPI integration, parsing, or code generation features.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade checks failing on main branch

1 participant