Skip to content

feat(mcp-gateway): reuse route resolutions and meter runtime traffic - #8034

Closed
iscekic wants to merge 6 commits into
mainfrom
drain/mcp-gateway-request-hardening
Closed

iscekic wants to merge 6 commits into
mainfrom
drain/mcp-gateway-request-hardening

Conversation

@iscekic

@iscekic iscekic commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Tokenless requests to the scoped connect and metadata routes resolve the route through a short-lived in-isolate cache with a bounded entry count; repeat requests for the same route reuse the stored result. The scoped surfaces pass a per-client-IP rate-limit binding, and authenticated proxied requests pass per-user and per-config windows after token verification, resolve the route once per request, and record one usage data point in the bound analytics dataset. Upstream hostname validation outcomes are cached per hostname for a short bounded window. Health and generic metadata stay unlimited.

Verification

Unit: pnpm --filter cloudflare-mcp-gateway test — 31 tests pass (was 18; new tests cover cache reuse for known and unknown routes, metadata-route cache reuse, 429 responses on the limited surfaces, both limiter keys consumed before runtime-state loading, usage data point content, hostname validation cache reuse/expiry/negative-caching). typecheck and lint clean; repo-wide pnpm typecheck clean.

E2E on this VM (wrangler dev --env dev + local Postgres + query counting via pg_stat_statements):

  • Repeat tokenless requests to a route that does not exist: every response 404, and the route query runs exactly once for the whole repetition (again after the cache window expires).
  • Seeded active route: 401 challenge with an intact WWW-Authenticate header on every request, one query for the repeated requests inside the window.
  • Requests beyond the anonymous window: the allowed count succeeds with 404, the remainder receive 429, and the database query count matches the allowed count only.
  • Scoped metadata route behaves the same through the cache; health and generic metadata stay 200 and unlimited.
  • Route rotation inside the cache window serves the stored result; after the 5s TTL the rotated route returns 404 (staleness bounded by TTL).
  • Authenticated runtime path with a locally minted gateway token: 403 (no seeded instance) with both limiter keys consumed before database work; a sustained sequence over the window produced the expected 429 share and exactly one route query per allowed request, none per limited one.
  • Health endpoint 200 throughout.

Proxy answers

Question: Where do the route and hostname caches live?
Answer: In-isolate bounded maps with named TTL constants (in-isolate Map).

Question: What transport enforces the request windows?
Answer: Cloudflare rate-limit bindings, one for the anonymous surfaces and one for the runtime surface (CF rate-limit binding).

Question: What does a request beyond the anonymous window receive?
Answer: HTTP 429 with a rate_limited JSON error body (429 rate_limited).

Question: What cache freshness applies to the unauthenticated route decision?
Answer: A 5-second window for that decision; authenticated requests keep the per-request fresh route recheck and the full eligibility recheck (5s TTL).

Tokenless requests to scoped connect and metadata routes consult a
short-lived in-isolate route cache with a bounded entry count; repeat
lookups for the same route reuse the stored result.

Anonymous traffic on those surfaces passes a per-client-IP rate-limit
binding; health and generic metadata stay unlimited.

Authenticated proxied requests pass per-user and per-config rate-limit
bindings after token verification, resolve the active route once per
request, and record one usage data point per proxied request in the bound
analytics dataset.

Upstream hostname validation results are cached per hostname for a short
bounded window across both address families.
@iscekic iscekic added drain Work item from the findings drain janitor:trust Janitor finding: trust boundary hardening labels Oct 19, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change removes the duplicated status blob from the proxy usage data point, leaving the numeric status only in doubles; the remaining call site and test expectations still line up.

Files Reviewed (1 file)
  • services/mcp-gateway/src/lib/usage.ts
Previous Review Summaries (4 snapshots, latest commit 4f7f51e)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 4f7f51e)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change charges the client-IP window when a bearer credential fails verification, closing the previously reported rate-limit bypass; the new branch is covered by a focused regression test.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (2 files)
  • services/mcp-gateway/src/handlers/connect.handler.ts
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts

Previous review (commit ca7f2c7)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The earlier Authorization bypass is narrowed but not closed: any parsed-but-invalid bearer token still skips the anonymous IP window and never reaches the authenticated runtime windows.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
services/mcp-gateway/src/handlers/connect.handler.ts 167 Bearer <junk> skips the IP window and is never charged to a runtime window because verification fails first
Files Reviewed (4 files)
  • services/mcp-gateway/src/handlers/connect.handler.ts - 1 issue
  • services/mcp-gateway/src/lib/usage.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit d97c480)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The new bearer-header bypass in anonymousClientLimiter lets unauthenticated callers skip the per-IP window entirely, restoring the unbounded route-lookup path the limiter exists to prevent.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
services/mcp-gateway/src/lib/usage.ts 36 Any non-empty Authorization header bypasses the tokenless IP window before token verification
Files Reviewed (5 files)
  • services/mcp-gateway/src/handlers/connect.handler.ts - 0 issues (claims/route match now precedes runtime limits)
  • services/mcp-gateway/src/lib/url-policy.ts - 0 issues (cached failure reason now preserved)
  • services/mcp-gateway/src/lib/usage.ts - 1 issue
  • services/mcp-gateway/src/lib/upstream-proxy.test.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit 29c7bca)

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

The new anonymous per-IP limiter also throttles authenticated runtime traffic to 60 req/min, and runtime limits are consumed before token/route matching, allowing another config's window to be exhausted.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
services/mcp-gateway/src/mcp-gateway.worker.ts 26 Anonymous per-IP limiter applies to authenticated /mcp-connect/* traffic, so 60/min per IP overrides the 600/min runtime window
services/mcp-gateway/src/handlers/connect.handler.ts 196 Per-config limit is consumed from the URL configId before claims/route matching, allowing another config's window to be exhausted with rejected requests

SUGGESTION

File Line Issue
services/mcp-gateway/src/lib/url-policy.ts 135 Cached negative DNS outcome always reports the generic validation message
Files Reviewed (11 files)
  • services/mcp-gateway/src/db/runtime-repository.ts - 0 issues
  • services/mcp-gateway/src/handlers/connect.handler.ts - 1 issue
  • services/mcp-gateway/src/handlers/protected-resource.handler.ts - 0 issues
  • services/mcp-gateway/src/lib/route-cache.ts - 0 issues (bounded at 1,000 entries; no leak)
  • services/mcp-gateway/src/lib/upstream-proxy.test.ts - 0 issues
  • services/mcp-gateway/src/lib/url-policy.ts - 1 issue (bounded at 200 entries; no leak)
  • services/mcp-gateway/src/lib/usage.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.ts - 1 issue
  • services/mcp-gateway/worker-configuration.d.ts - generated, 0 issues
  • services/mcp-gateway/wrangler.jsonc - 0 issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash-1017 · Input: 40.9K · Output: 2K · Cached: 265.5K

Review guidance: REVIEW.md from base branch main

Comment thread services/mcp-gateway/src/lib/usage.ts Resolved
@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 19, 2026
@iscekic
iscekic enabled auto-merge October 19, 2026 17:45
@iscekic

iscekic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Closing this pull request as obsolete.

main removed the MCP Gateway feature end to end in #8156 (services/mcp-gateway, the Next.js control plane, packages/mcp-gateway) and #8157 (its database tables). main is now at 2d93713. Every path this branch changes was deleted by those commits, which is why the pull request reports a merge conflict: each changed file is a modify/delete against main.

Changed paths, all absent on main:

  • services/mcp-gateway/src/db/runtime-repository.ts
  • services/mcp-gateway/src/handlers/connect.handler.ts
  • services/mcp-gateway/src/handlers/protected-resource.handler.ts
  • services/mcp-gateway/src/lib/route-cache.ts
  • services/mcp-gateway/src/lib/upstream-proxy.test.ts
  • services/mcp-gateway/src/lib/url-policy.ts
  • services/mcp-gateway/src/lib/usage.ts
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts
  • services/mcp-gateway/src/mcp-gateway.worker.ts
  • services/mcp-gateway/worker-configuration.d.ts
  • services/mcp-gateway/wrangler.jsonc

There is no code on main for this branch to apply to, so closing is the end state. The branch is left as pushed at head 2765239; nothing is merged and no new commits are added.

@iscekic iscekic closed this Oct 19, 2026
auto-merge was automatically disabled October 19, 2026 22:52

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:trust Janitor finding: trust boundary hardening merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant