Skip to content

Reject on /api/eth-rpc in direct_api_call#389

Merged
akolotov merged 8 commits into
mainfrom
claude/serene-taussig-faaa53
Jun 3, 2026
Merged

Reject on /api/eth-rpc in direct_api_call#389
akolotov merged 8 commits into
mainfrom
claude/serene-taussig-faaa53

Conversation

@akolotov
Copy link
Copy Markdown
Collaborator

@akolotov akolotov commented Jun 3, 2026

Closes #386.

Motivation

  • During the migration to the single Blockscout PRO API (Switch to PRO API #384), the JSON-RPC entry point of direct_api_call moved from the historical /api/eth-rpc path to /json-rpc. A caller (human or agent) that still requests the old path would previously hit one of two poor outcomes: a silent rewrite (opaque, and hides the convention change) or an opaque upstream gateway error — and either way, the request consumed an authenticated PRO API credit on something known to fail.
  • This change adds an explicit, pre-network rejection: when /api/eth-rpc is still requested, the tool fails fast with a corrective error that names the supported /json-rpc path, before any network call and before any credit is spent.
  • The guidance is surfaced as a runtime error — a just-in-time teaching signal delivered exactly when the mistake is made — and documented in SPEC.md. It is deliberately not added to the tool's docstring/description: that text loads into every agent's context on every call, so documenting a rarely-needed legacy-path mistake there would pollute context for no benefit.

Description

  • Phase 1 — Reject the legacy JSON-RPC path: In direct_api_call, added the rejection inside the existing path-validation block, immediately after whitespace/trailing-slash normalization and before the ?-in-path check so the corrective legacy-path message wins over the generic query-param error. endpoint_path is stripped of surrounding whitespace once up front (the same normalization then applies to every path, not just the legacy one), and a single normalized comparison — endpoint_path.split("?", 1)[0].rstrip("/").lower() == "/api/eth-rpc" — folds the bare path, the trailing-slash variant (/api/eth-rpc/), capitalization variants (/API/ETH-RPC), and a trailing query string (/api/eth-rpc?id=1) onto one rejection. On a match it raises ValueError("The legacy JSON-RPC path '/api/eth-rpc' is no longer supported. Retry with endpoint_path='/json-rpc'.") — it never silently rewrites the path; transparency is the point. Placing the guard before the network call (make_blockscout_request / make_blockscout_post_request) is what guarantees no credit is spent. Added a focused unit-test module tests/tools/direct_api/test_direct_api_call_validation.py (a new file, since test_direct_api_call.py is already at the 500-LOC cap) with nine tests: six rejection cases (GET bare path, trailing-slash, POST, case-insensitive, trailing query string, surrounding whitespace), each asserting the network helper was not awaited; and three pass-through cases (a non-legacy look-alike /api/eth-rpc-foo, the supported /json-rpc path, and a whitespace-padded supported path whose api_path is normalized before the network call).
  • Phase 2 — Documentation: Extended the direct_api_call Implementation paragraph in SPEC.md with two sentences describing the legacy-path rejection as part of the same pre-network validation, and added the new test module to the AGENTS.md project-structure tree. No agent-facing context surface was touched: the tool docstring, API.md, gpt/*, and TESTING.md are unchanged (they already reference /json-rpc only). API.md needs no change because the operation's contract (path, method, parameters, response shape) is unchanged — only the runtime behavior for one specific input value is refined.
  • Phase 3 — Version bump: Bumped the server version from 0.16.0.dev11 to 0.16.0.dev12 in the three synced locations (pyproject.toml, blockscout_mcp_server/__init__.py, server.json). mcpb/manifest.json is intentionally left unchanged (rule 135): no tool was added or removed and the tool description is unchanged.

Testing

  • Unit tests: full suite via uv run pytest -m "not integration" — green, including the nine new validation tests.
  • Integration tests: run through the timeout-protected runner (uv run python scripts/run_integration_tests.py); the existing test_direct_api_call_real.py already exercises the supported /json-rpc path. No new integration test was added — the new behavior is pure offline input validation with no network call.
  • Lint / format: uv run ruff check . and uv run ruff format --check . both clean.

Summary by CodeRabbit

  • New Features

    • The direct_api_call tool now validates endpoint paths before processing requests, rejecting legacy paths and providing guidance to use the correct endpoint.
    • Endpoint paths are automatically normalized by removing whitespace and trailing slashes.
  • Chores

    • Updated package version to 0.16.0.dev12.

akolotov and others added 3 commits June 2, 2026 19:11
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@akolotov akolotov self-assigned this Jun 3, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Warning

Review limit reached

@akolotov, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 59 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f79b5824-be2b-45c2-9f36-4c45405e2c0e

📥 Commits

Reviewing files that changed from the base of the PR and between e385f57 and d902bd5.

📒 Files selected for processing (2)
  • SPEC.md
  • tests/tools/direct_api/test_direct_api_call_validation.py

Walkthrough

The PR implements an explicit corrective rejection for requests to the legacy Blockscout JSON-RPC endpoint /api/eth-rpc in the direct_api_call tool. The implementation detects the legacy path, normalizes input, and fails fast with a clear message directing callers to the supported /json-rpc endpoint—avoiding silent rewrites and wasted upstream API calls.

Changes

Legacy JSON-RPC path validation and rejection

Layer / File(s) Summary
Path validation implementation
blockscout_mcp_server/tools/direct_api/direct_api_call.py
The direct_api_call function now normalizes endpoint_path by stripping whitespace and removing trailing slashes, then performs an early, case-insensitive rejection of requests to /api/eth-rpc with a ValueError and guidance to use /json-rpc, ensuring this corrective error surfaces before generic query-string validation or network requests.
Path validation test suite
tests/tools/direct_api/test_direct_api_call_validation.py
Comprehensive pytest module with eleven async test cases covering eight variations of the legacy /api/eth-rpc rejection (GET/POST, case-insensitive matching, trailing slashes, query strings, surrounding whitespace), two cases permitting non-legacy lookalike paths and /json-rpc to proceed to the mocked network call, and one case asserting whitespace normalization around valid paths.
Specification documentation
SPEC.md
The direct_api_call tool's "Implementation" section now documents the new validation rule: requests to /api/eth-rpc are rejected before any network call and must be redirected to /json-rpc, while also restating strict POST validation, conservative retry semantics, and response-size-limit behavior.
Version and metadata updates
blockscout_mcp_server/__init__.py, pyproject.toml, server.json, AGENTS.md
Package version incremented from 0.16.0.dev11 to 0.16.0.dev12 across all version files, and the new test module is registered in the project tree documentation.

Possibly Related PRs

  • blockscout/mcp-server#364: Extends direct_api_call endpoint-path validation to reject legacy /api/eth-rpc for POST requests and JSON-RPC calls, overlapping with this PR's validation scope.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Reject on /api/eth-rpc in direct_api_call' clearly and concisely describes the primary change—adding legacy path rejection to the direct_api_call tool.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #386: detects and rejects the legacy /api/eth-rpc path before network activity, raises a clear corrective error directing callers to /json-rpc, includes comprehensive test coverage, and documents the change.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #386: path validation logic, test coverage, documentation updates (SPEC.md, AGENTS.md), and version bumps—no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/serene-taussig-faaa53

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.

akolotov and others added 3 commits June 3, 2026 10:49
Normalize endpoint_path (strip query string, whitespace, trailing slash,
case) before comparing to the legacy path, so the corrective /json-rpc
message wins over the generic query-param error and surrounding-whitespace
variants are caught too. Add tests for the query-string and whitespace
variants, plus negative tests confirming a lookalike (/api/eth-rpc-foo) and
the supported /json-rpc path are not rejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ess count

- Strip surrounding whitespace from endpoint_path once up front so supported
  paths are normalized the same way as the legacy-path guard (previously only
  the rejection comparison trimmed whitespace, leaving "  /json-rpc  " to fail
  opaquely upstream).
- Drop the now-redundant .strip() from the guard comparison; the remaining
  .rstrip("/") still handles the slash-before-query case (/api/eth-rpc/?id=1).
- Remove the report_progress.await_count assertions from the rejection tests;
  assert_not_awaited() already proves the rejection is pre-network, and the
  count coupled the tests to the internal progress sequence.
- Add a test asserting whitespace on a supported path is stripped before the
  network call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Strip whitespace from the pre-query segment so "/api/eth-rpc ?id=1"
folds onto the same rejection instead of falling through to the generic
query-param error. Add two rejection tests: trailing-slash + query
string, and whitespace before the query string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@akolotov akolotov marked this pull request as ready for review June 3, 2026 17:12
@akolotov
Copy link
Copy Markdown
Collaborator Author

akolotov commented Jun 3, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/tools/direct_api/test_direct_api_call_validation.py`:
- Around line 155-170: The test currently only asserts that
make_blockscout_request was awaited once; update
test_direct_api_call_allows_non_legacy_lookalike_path to assert the exact helper
call arguments by checking mock_get.assert_awaited_once_with(...) so the call
from direct_api_call(chain_id="1", endpoint_path="/api/eth-rpc-foo",
ctx=mock_ctx) passes the exact api_path and params contract to
make_blockscout_request (use the api_path value derived from endpoint_path and
the params derived from chain_id/ctx used by direct_api_call); do the same style
of assertion for the other test(s) mentioned (around lines 173-190) to lock down
normalization/regression scenarios.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d3d84af3-ab4e-4879-84ce-72dffb081689

📥 Commits

Reviewing files that changed from the base of the PR and between 8dba3f3 and e385f57.

📒 Files selected for processing (7)
  • AGENTS.md
  • SPEC.md
  • blockscout_mcp_server/__init__.py
  • blockscout_mcp_server/tools/direct_api/direct_api_call.py
  • pyproject.toml
  • server.json
  • tests/tools/direct_api/test_direct_api_call_validation.py

Comment thread tests/tools/direct_api/test_direct_api_call_validation.py Outdated
akolotov and others added 2 commits June 3, 2026 16:37
…ests

Tighten the three pass-through tests from assert_awaited_once() to
assert_awaited_once_with(...) so they lock down the exact api_path/params
(and json_body for POST) contract passed to the network helper. This guards
the look-alike path against silent rewrites and pins normalization behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Condense the two-sentence description to a single clause that keeps the
behavioral contract (pre-network rejection, error names /json-rpc, never
silently rewritten) and drops the PR-rationale editorializing, matching the
density of the surrounding Implementation paragraph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@akolotov akolotov merged commit f52e8fc into main Jun 3, 2026
7 checks passed
@akolotov akolotov deleted the claude/serene-taussig-faaa53 branch June 3, 2026 23:19
akolotov added a commit that referenced this pull request Jun 3, 2026
main already shipped 0.16.0.dev12 via #389 after this branch diverged,
so re-bump to dev13 to keep the dev version monotonic on merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reject the legacy JSON-RPC path in direct_api_call with a corrective error

1 participant