-
Notifications
You must be signed in to change notification settings - Fork 9
Added direct_api_call
tool
#216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a new direct_api_call tool (implementation, models, constants), exposes it via a REST route, injects curated endpoint rules into initialization output, augments address/transaction tools with follow-up instructions, updates docs/manifests/OpenAPI/templates, and adds unit, integration, and route tests plus version bumps. Changes
Sequence Diagram(s)%%{init: {"themeVariables":{"actorBorder":"#2b6cb0","actorFill":"#e6f2ff","noteBorder":"#a0aec0","noteText":"#2d3748"}} }%%
sequenceDiagram
autonumber
participant Client as Client
participant REST as REST Router (/v1/direct_api_call)
participant Tool as direct_api_call
participant BS as Blockscout API
Client->>REST: GET /v1/direct_api_call?chain_id=&endpoint_path=&cursor?&query_params...
REST->>REST: Validate chain_id & endpoint_path
REST->>Tool: direct_api_call(chain_id, endpoint_path, query_params, cursor, ctx)
Tool->>Tool: validate path → apply cursor → merge params
Tool->>BS: make_blockscout_request(base_url, endpoint_path, params)
BS-->>Tool: JSON response (+ next_page_params?)
Tool->>Tool: build next_cursor & NextCallInfo (if paginated)
Tool-->>REST: ToolResponse {data, pagination?}
REST-->>Client: 200 JSON ToolResponse
%%{init: {"themeVariables":{"actorBorder":"#2b6cb0","actorFill":"#e6f2ff","noteBorder":"#a0aec0","noteText":"#2d3748"}} }%%
sequenceDiagram
autonumber
participant AI as AI/Agent
participant Init as __unlock_blockchain_analysis__
participant Cfg as Constants (DIRECT_API_CALL_*)
participant MCP as MCP Server
AI->>Init: request unlock
Init->>Cfg: read DIRECT_API_CALL_RULES & ENDPOINT_LIST
Init-->>AI: InstructionsData {direct_api_call_rules, direct_api_endpoints, ...}
note over AI,MCP: Tools may include contextual `instructions` suggesting `direct_api_call`
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Out-of-scope changes (observations)
Possibly related PRs
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tests/tools/test_direct_api_tools.py (2)
103-109
: Tighten pagination assertions and verify single request + progressAlso assert chain_id/endpoint_path are carried in next_call and that mocks were called once.
Apply:
result = await direct_api_call(chain_id=chain_id, endpoint_path=endpoint_path, ctx=mock_ctx) assert isinstance(result, ToolResponse) assert result.pagination is not None assert result.pagination.next_call.tool_name == "direct_api_call" - assert "cursor" in result.pagination.next_call.params + nc = result.pagination.next_call.params + assert nc["chain_id"] == chain_id + assert nc["endpoint_path"] == endpoint_path + assert "cursor" in nc + # No query_params in next_call when none were provided + assert "query_params" not in nc + # Single network call, and 3 progress updates + mock_get_url.assert_called_once_with(chain_id) + mock_request.assert_called_once_with(base_url=mock_base_url, api_path=endpoint_path, params={}) + assert mock_ctx.report_progress.await_count == 3
1-109
: Add error-path unit test (timeout/exception propagation)Per guidelines, include error cases. Verify exceptions are raised (not wrapped into ToolResponse).
Add this test:
@@ @pytest.mark.asyncio async def test_direct_api_call_with_pagination(mock_ctx): @@ assert "cursor" in result.pagination.next_call.params + +@pytest.mark.asyncio +async def test_direct_api_call_raises_on_request_error(mock_ctx): + chain_id = "1" + endpoint_path = "/api/v2/data" + mock_base_url = "https://eth.blockscout.com" + with ( + patch("blockscout_mcp_server.tools.direct_api_tools.get_blockscout_base_url", new_callable=AsyncMock) as mock_get_url, + patch("blockscout_mcp_server.tools.direct_api_tools.make_blockscout_request", new_callable=AsyncMock) as mock_request, + ): + mock_get_url.return_value = mock_base_url + mock_request.side_effect = TimeoutError("upstream timeout") + with pytest.raises(TimeoutError): + await direct_api_call(chain_id=chain_id, endpoint_path=endpoint_path, ctx=mock_ctx) + mock_get_url.assert_called_once_with(chain_id) + mock_request.assert_awaited_once() + # Even on error, two progress reports should have occurred (resolve URL, start fetch) + assert mock_ctx.report_progress.await_count >= 2
🧹 Nitpick comments (21)
blockscout_mcp_server/llms.txt (1)
62-62
: Add a brief note to steer pagination usage (avoid unsupported limit).Keeps agent guidance aligned with tests removing
limit
.Apply this diff:
-18. **`direct_api_call`** - Calls a curated raw Blockscout API endpoint +18. **`direct_api_call`** - Calls a curated raw Blockscout API endpoint. Do not pass a `limit` parameter; page size is set by the server—advance using the returned `cursor`.API.md (3)
497-504
: Fix markdownlint violation by using real subheadings; keep param table intactReplace emphasized labels with proper H4 headings to satisfy MD036; table content is fine.
-**Parameters** +#### Parameters @@ -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| `chain_id` | `string` | Yes | The ID of the blockchain. | -| `endpoint_path` | `string` | Yes | The Blockscout API path to call (e.g., `/api/v2/stats`). | -| `cursor` | `string` | No | The cursor for pagination from a previous response. | +| Name | Type | Required | Description | +| ------------- | -------- | -------- | -------------------------------------------------------- | +| `chain_id` | `string` | Yes | The ID of the blockchain. | +| `endpoint_path` | `string` | Yes | The Blockscout API path to call (e.g., `/api/v2/stats`). | +| `cursor` | `string` | No | The cursor for pagination from a previous response. |
505-506
: Remove parameter-behavior prose to keep API.md concise per house styleAPI.md should be brief and avoid explaining parameter behavior. Consider moving this note to TESTING.md or SPEC.md.
-Any additional query parameters appended to the URL are forwarded directly to the Blockscout API. +<!-- Additional query parameters are supported; see TESTING.md for examples. -->
507-511
: Include an example showing the optional cursor; convert label to a proper headingAdd a second curl with cursor to demonstrate pagination, and convert the label to H4 heading.
-**Example Request** +#### Example Request @@ ```bash curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats"
+#### Example Request (with pagination cursor)
+
+bash +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats&cursor=eyJwYWdlIjoyfQ==" +
</blockquote></details> <details> <summary>TESTING.md (1)</summary><blockquote> `230-235`: **Augment the Direct API Call example with forwarded params and pagination** Demonstrate a realistic call with extra query params and a cursor to mirror production usage. ```diff #### 6. Direct API Call ```bash -curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats" +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/tokens/0xdAC17F958D2ee523a2206206994597C13D831ec7/holders&age_from=2024-01-01T00:00:00Z" + +# Example: next page using the returned cursor (value shown here is illustrative) +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/tokens/0xdAC17F958D2ee523a2206206994597C13D831ec7/holders&cursor=eyJwYWdlIjozfQ=="
</blockquote></details> <details> <summary>gpt/instructions.md (2)</summary><blockquote> `78-141`: **Validate endpoint path accuracy and placeholders** Some paths are easy to mistype (e.g., placeholder names or singular/plural segments). Please verify each against the live API or the constants source. Would you like me to open a follow-up to add a small schema (name, path template, brief description) and auto-render this section from the constants to eliminate manual edits? --- `59-76`: **Sync direct_api_call endpoint docs with code constants** Our diff script shows the static list in gpt/instructions.md under `<direct_call_endpoint_list>` is significantly out-of-sync with the `DIRECT_API_CALL_ENDPOINT_LIST` constants in the code. Many endpoints are missing, and stale descriptions remain. To prevent this drift going forward: - Generate the markdown list directly from the source constants (e.g., a small build-time utility that emits the paths and descriptions). - Or add a CI check (using the above comparison script) to fail the build if `gpt/instructions.md` and the code constants diverge. - If you continue maintaining the list by hand, update it now to include all current endpoints and prune deprecated ones whenever code changes. </blockquote></details> <details> <summary>AGENTS.md (1)</summary><blockquote> `285-287`: **Keep tools list consistent with tests section.** Please also list tests/integration/test_direct_api_tools_integration.py in the Integration Tests section for parity with other tools. </blockquote></details> <details> <summary>blockscout_mcp_server/tools/direct_api_tools.py (2)</summary><blockquote> `7-14`: **Use apply_cursor_to_params for cursor merging (consistency with other tools).** Centralize cursor handling via the shared helper. Apply this diff: ```diff from blockscout_mcp_server.tools.common import ( build_tool_response, - decode_cursor, encode_cursor, get_blockscout_base_url, make_blockscout_request, report_and_log_progress, + apply_cursor_to_params, ) @@ - params = dict(query_params) if query_params else {} - if cursor: - params.update(decode_cursor(cursor)) + params = dict(query_params) if query_params else {} + apply_cursor_to_params(cursor, params)
Also applies to: 41-44
21-26
: Clarify endpoint_path usage (no query string).To avoid confusion and double-encoding, reject endpoint_path containing '?' and instruct callers to use query_params for all query args.
Apply this guard:
endpoint_path: Annotated[str, Field(description="The Blockscout API path to call (e.g., '/api/v2/stats')")], @@ ) -> ToolResponse[dict[str, Any]]: @@ - base_url = await get_blockscout_base_url(chain_id) + base_url = await get_blockscout_base_url(chain_id) + if "?" in endpoint_path: + raise ValueError("Do not include query parameters in endpoint_path. Use query_params instead.")README.md (1)
150-151
: Add a short note about query_params vs query string.To guide users, append “Pass all query parameters via query_params; do not append them to endpoint_path.”
tests/tools/test_transaction_tools_2.py (1)
82-87
: Make assertion resilient to minor copy changes in instruction textMatch on the endpoint substring rather than the full sentence to avoid brittle failures if wording changes.
- expected_instruction = ( - "Use `direct_api_call` with endpoint " - f"`/api/v2/proxy/account-abstraction/operations?transaction_hash={hash}` " - "to get User Operations for this transaction." - ) - assert result.instructions is not None and expected_instruction in result.instructions + endpoint_hint = f"/api/v2/proxy/account-abstraction/operations?transaction_hash={hash}" + assert result.instructions is not None + assert any(endpoint_hint in s for s in result.instructions)tests/api/test_routes.py (1)
558-572
: Avoid using 'limit' in direct_api_call tests; prefer a commonly supported paramTo align with the PR goal of removing unsupported 'limit' usage, use a neutral key like page_size for the passthrough query param.
- url = "/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats&limit=1" + url = "/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats&page_size=1" @@ - query_params={"limit": "1"}, + query_params={"page_size": "1"},blockscout_mcp_server/tools/transaction_tools.py (1)
548-556
: Clarify instruction as ERC-4337-specific and shorten wordingThis avoids implying all transactions have UserOps and keeps the hint crisp. If you adopt this, pair it with the test tweak suggested in tests/tools/test_transaction_tools_2.py.
- instructions = [ - ( - "Use `direct_api_call` with endpoint " - f"`/api/v2/proxy/account-abstraction/operations?transaction_hash={transaction_hash}` " - "to get User Operations for this transaction." - ) - ] + instructions = [ + ( + "To check for ERC-4337 User Operations related to this tx, call " + f"`direct_api_call` with endpoint " + f"`/api/v2/proxy/account-abstraction/operations?transaction_hash={transaction_hash}`." + ) + ]blockscout_mcp_server/api/routes.py (1)
268-279
: Preserve multi-valued query params in REST wrapper.
dict(request.query_params)
collapses repeated keys (e.g.,topics=...&topics=...
). Some Blockscout endpoints accept repeated params. Capture multi-values to lists and pass them through.- extra = dict(request.query_params) - for key in ["chain_id", "endpoint_path", "cursor"]: - extra.pop(key, None) - if extra: - params["query_params"] = dict(extra) + # Preserve multi-valued params (e.g., key=a&key=b -> {"key": ["a", "b"]}) + ignored = {"chain_id", "endpoint_path", "cursor"} + extra: dict[str, Any] = {} + for key in request.query_params: + if key in ignored: + continue + values = request.query_params.getlist(key) + extra[key] = values if len(values) > 1 else values[0] + if extra: + params["query_params"] = extratests/tools/test_address_tools.py (1)
435-457
: Verify endpoint path consistency for coin balance history.One instruction uses
/addresses/{address}/coin-balance-history
(no/api/v2
). Other instructions use/api/v2/...
. Confirm the actual endpoint path exposed by Blockscout and align the expected string. Consider accepting either variant to reduce brittleness.Example (accept either path in assertion):
- expected_instructions = [ + expected_instructions = [ (f"Use `direct_api_call` with endpoint `/api/v2/addresses/{address}/logs` to get Logs Emitted by Address."), ( f"Use `direct_api_call` with endpoint `/api/v2/addresses/{address}/coin-balance-history-by-day` " "to get daily native coin balance history." ), - ( - f"Use `direct_api_call` with endpoint `/addresses/{address}/coin-balance-history` " - "to get native coin balance history." - ), + # Some instances expose this under /api/v2, others without the prefix + ( + f"Use `direct_api_call` with endpoint `/api/v2/addresses/{address}/coin-balance-history` " + "to get native coin balance history." + ),Or:
# Alternative: assert either form is present alts = [ f"Use `direct_api_call` with endpoint `/api/v2/addresses/{address}/coin-balance-history` to get native coin balance history.", f"Use `direct_api_call` with endpoint `/addresses/{address}/coin-balance-history` to get native coin balance history.", ] assert any(a in result.instructions for a in alts)blockscout_mcp_server/constants.py (1)
164-166
: Placeholder name nit: transactions_hash → transaction_hash.For consistency with common naming elsewhere.
- "path": "/api/v2/arbitrum/messages/withdrawals/{transactions_hash}", - "description": "Get L2 to L1 messages for a specific transaction hash on Arbitrum.", + "path": "/api/v2/arbitrum/messages/withdrawals/{transaction_hash}", + "description": "Get L2 to L1 messages for a specific transaction hash on Arbitrum.",tests/integration/test_direct_api_tools_integration.py (1)
29-35
: Harden integration tests against transient network/pagination variance.Add simple retries for
httpx.RequestError
and skip if no pagination after a few attempts to reduce flakiness.@@ @pytest.mark.integration @pytest.mark.asyncio async def test_direct_api_call_blocks_validated_pagination(mock_ctx): path = "/api/v2/addresses/0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97/blocks-validated" - first = await direct_api_call(chain_id="1", endpoint_path=path, ctx=mock_ctx) + # Basic retry on transport errors + attempts = 0 + while True: + attempts += 1 + try: + first = await direct_api_call(chain_id="1", endpoint_path=path, ctx=mock_ctx) + break + except Exception as e: + if attempts >= 3: + pytest.skip(f"Network instability or upstream issue: {e}") + continue assert first.pagination is not None next_params = first.pagination.next_call.params second = await direct_api_call(ctx=mock_ctx, **next_params) - assert isinstance(second.data, dict) + assert isinstance(second.data, dict) + # Optional: sanity-check the second page differs from the first when feasible + if isinstance(first.data, dict) and isinstance(second.data, dict): + if "items" in first.data and "items" in second.data: + assert first.data["items"] != second.data["items"], "Second page should differ from first when paginated"SPEC.md (1)
585-611
: Document explicit pagination support fordirect_api_call
.To match our tool-doc guidance, add an explicit note that the tool supports pagination via the
pagination.next_call
cursor.-### Direct API Call Tool (`direct_api_call`) +### Direct API Call Tool (`direct_api_call`) + +SUPPORTS PAGINATION: If the response includes a `pagination` field, use the provided `next_call` (with `cursor`) to fetch additional pages.blockscout_mcp_server/server.py (2)
51-62
: Format function couples to dict shape; consider typed models or shared rendererformat_endpoint_groups assumes dicts with "group"/"chains_family". Prefer operating on DirectApiCommonGroup/DirectApiSpecificGroup or centralizing this rendering to avoid drift with constants/models.
Example refactor:
-def format_endpoint_groups(groups): +def format_endpoint_groups(groups) -> str: formatted = [] - for group in groups: - if "group" in group: - formatted.append(f'<group name="{group["group"]}">') - formatted.extend(f'"{endpoint["path"]}" - "{endpoint["description"]}"' for endpoint in group["endpoints"]) + for group in groups: + if hasattr(group, "group"): + formatted.append(f'<group name="{getattr(group, "group")}">') + endpoints = getattr(group, "endpoints") + formatted.extend(f'"{e.path}" - "{e.description}"' for e in endpoints) elif "chains_family" in group: formatted.append(f'<chains_family name="{group["chains_family"]}">') formatted.extend(f'"{endpoint["path"]}" - "{endpoint["description"]}"' for endpoint in group["endpoints"]) formatted.append("</chains_family>") return "\n".join(formatted)Or render from the already-typed structure produced by unlock_blockchain_analysis to keep one source of truth.
65-66
: Avoid recomputing lists from raw constantscommon_endpoints/specific_endpoints are derived from constants again here, while initialization_tools builds the typed list for instructions. Consider importing the typed structure or moving the rendering to a shared util to prevent duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
API.md (2)
499-505
: Clarify query_params encoding and scope.
- Consider a short note that query parameters are passed via bracket syntax (e.g., query_params[page]=1) and that only curated endpoints are supported by the server (to prevent misuse of arbitrary paths).
Apply this minimal tweak:
-| `endpoint_path` | `string` | Yes | The Blockscout API path to call (e.g., `/api/v2/stats`). | -| `query_params` | `object` | No | Additional query parameters forwarded to the Blockscout API. | +| `endpoint_path` | `string` | Yes | The Blockscout API path to call from the curated allowlist (e.g., `/api/v2/stats`). | +| `query_params` | `object` | No | Additional query parameters forwarded to the Blockscout API. Use bracket syntax in the query string, e.g., `query_params[page]=1`. |
506-510
: Optional: silence MD036 or align headings repo-wide.markdownlint flags “Parameters” and “Example Request” as emphasis-as-heading. To stay consistent with the rest of this file, either:
- Keep current style and add a top-of-file markdownlint disable for MD036, or
- Convert all sections in API.md to real headings in a separate cleanup PR.
No change required here if you prefer consistency.
tests/api/test_routes.py (2)
558-572
: Align test with PR goal: avoid unsupportedlimit
; usepage
instead.The PR removes
limit
usage in pagination tests. Update this unit test to avoid signalinglimit
as supported and keep consistency with API.md.Apply:
- url = "/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats&query_params[limit]=1" + url = "/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats&query_params[page]=1" @@ - query_params={"limit": "1"}, + query_params={"page": "1"},
558-572
: Cover “required-only” happy path.Per route-test guidelines, add a success test with only required params (no query_params, no cursor).
Add:
@pytest.mark.asyncio @patch("blockscout_mcp_server.api.routes.direct_api_call", new_callable=AsyncMock) async def test_direct_api_call_required_only(mock_tool, client: AsyncClient): mock_tool.return_value = ToolResponse(data={"ok": True}) response = await client.get("/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/stats") assert response.status_code == 200 assert response.json()["data"] == {"ok": True} mock_tool.assert_called_once_with(chain_id="1", endpoint_path="/api/v2/stats", ctx=ANY)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
API.md
(1 hunks)TESTING.md
(1 hunks)blockscout_mcp_server/api/routes.py
(3 hunks)blockscout_mcp_server/constants.py
(1 hunks)blockscout_mcp_server/models.py
(2 hunks)blockscout_mcp_server/server.py
(5 hunks)blockscout_mcp_server/tools/address_tools.py
(1 hunks)blockscout_mcp_server/tools/direct_api_tools.py
(1 hunks)blockscout_mcp_server/tools/initialization_tools.py
(4 hunks)gpt/instructions.md
(1 hunks)gpt/openapi.yaml
(1 hunks)tests/api/test_routes.py
(1 hunks)tests/integration/test_direct_api_tools_integration.py
(1 hunks)tests/tools/test_address_tools.py
(1 hunks)tests/tools/test_direct_api_tools.py
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- TESTING.md
🚧 Files skipped from review as they are similar to previous changes (12)
- blockscout_mcp_server/tools/address_tools.py
- blockscout_mcp_server/models.py
- blockscout_mcp_server/server.py
- blockscout_mcp_server/constants.py
- blockscout_mcp_server/tools/initialization_tools.py
- blockscout_mcp_server/api/routes.py
- gpt/openapi.yaml
- tests/tools/test_direct_api_tools.py
- tests/tools/test_address_tools.py
- gpt/instructions.md
- blockscout_mcp_server/tools/direct_api_tools.py
- tests/integration/test_direct_api_tools_integration.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/000-role-and-task.mdc)
**/*.py
: The MCP server must be implemented in Python, as you are a senior Python developer and the expertise is in Python.
The MCP server must wrap Blockscout APIs and expose blockchain data (balances, tokens, NFTs, contract metadata) via the Model Context Protocol (MCP).
The MCP server must communicate with AI agents/chat applications through stdin.
**/*.py
: Regular Python modules should generally not exceed 500 lines of code (LOC). If a module approaches this limit, consider splitting it into multiple focused modules (e.g., address_tools.py and address_tools_advanced.py) to maintain readability and logical organization.
ALL import statements must be placed at the top of the Python module, immediately after the module docstring (if present) and before any other code. Never insert imports inline near where the functionality is used. Follow PEP 8 import order.
ALL linting and formatting issues must be resolved before committing or pushing code. Use the Ruff rules defined in 300-ruff-lint-and-format.mdc to identify and fix issues.
**/*.py
: Always runruff check . --fix
andruff format .
on generated code before suggesting commits or opening a PR
Avoid using# noqa: E501
for ordinary code lines; split long lines instead. Only use# noqa: E501
for docstrings or string literals that must exceed 120 characters.
Use Ruff to enforce a 120-character line length, compatible with Black formatting
Files:
tests/api/test_routes.py
tests/api/test_routes.py
📄 CodeRabbit inference engine (.cursor/rules/200-development-testing-workflow.mdc)
tests/api/test_routes.py
: Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
tests/api/test_routes.py
: Create aFastMCP
instance and register routes usingregister_api_routes
before making requests in tests for the REST API
Usehttpx.AsyncClient
withASGITransport
to call the routes in REST API route tests
Patch the wrapped tool functions withunittest.mock.patch
andAsyncMock
to avoid real network calls in REST API route tests
Assert that each endpoint returns the expected HTTP status and JSON content in REST API route tests
Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Include error-case tests to confirm a400
response is returned when required query parameters are missing in REST API route tests
Test the static endpoints (e.g./health
,/
,/llms.txt
) to ensure they return the correct status code and content type afterregister_api_routes
is called, and confirm these routes are unavailable on a cleanFastMCP
instance before registration
For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP400
Name the test functions after the endpoint without the_rest
suffix (e.g., usetest_get_chains_list_success
instead oftest_get_chains_list_rest_success
)
Files:
tests/api/test_routes.py
API.md
📄 CodeRabbit inference engine (.cursor/rules/800-api-documentation-guidelines.mdc)
API.md
: Whenever a new MCP tool is added or an existing one is modified, its corresponding REST API endpoint in API.md MUST be added or updated.
Each endpoint documentation MUST follow the exact Markdown structure specified for consistency, including the heading format, parameter table, and example request.
The heading should be the human-readable tool name, with the function name in backticks.
The parameter table must clearly distinguish between required and optional parameters.
The curl example should demonstrate a realistic use case, including optional parameters where applicable.Update REST API documentation in API.md for each new or updated endpoint, following the API documentation guidelines.
Files:
API.md
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to blockscout_mcp_server/tools/*.py : For paginated tools, accept an optional cursor argument and use apply_cursor_to_params to handle incoming cursors.
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Patch the wrapped tool functions with `unittest.mock.patch` and `AsyncMock` to avoid real network calls in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP `400`
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Include error-case tests to confirm a `400` response is returned when required query parameters are missing in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Test the static endpoints (e.g. `/health`, `/`, `/llms.txt`) to ensure they return the correct status code and content type after `register_api_routes` is called, and confirm these routes are unavailable on a clean `FastMCP` instance before registration
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Name the test functions after the endpoint without the `_rest` suffix (e.g., use `test_get_chains_list_success` instead of `test_get_chains_list_rest_success`)
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Ensure all external API calls in tests are properly mocked using unittest.mock.patch and AsyncMock.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Assert that each endpoint returns the expected HTTP status and JSON content in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Assert that mocked API helper functions (such as make_blockscout_request) are called exactly once with the correct api_path and params in tests.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Use `httpx.AsyncClient` with `ASGITransport` to call the routes in REST API route tests
Applied to files:
tests/api/test_routes.py
🧬 Code graph analysis (1)
tests/api/test_routes.py (1)
blockscout_mcp_server/models.py (1)
ToolResponse
(375-400)
🪛 markdownlint-cli2 (0.17.2)
API.md
497-497: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
506-506: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Integration Tests
🔇 Additional comments (1)
API.md (1)
491-495
: Docs addition looks solid and consistent with the rest of API.md.Clear name, route, and short description. Nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
tests/api/test_routes.py (2)
573-588
: Cursor + query_params coverage — LGTM (addresses prior suggestion)Validates nested query param parsing and cursor forwarding; aligns with pagination contract and removes any reliance on the unsupported limit param.
Run to confirm no lingering uses of the unsupported "limit" parameter remain in tests:
#!/bin/bash # Search repo for any residual 'limit' usage in tests (query string or nested query_params) rg -nP -C2 '(?:^|[?&])limit=\d+|query_params\[(?:[^]]*limit[^]]*)\]' tests || true
597-602
: Missing chain_id negative test — LGTM (addresses prior suggestion)Good parallel coverage for the other required parameter.
🧹 Nitpick comments (1)
tests/api/test_routes.py (1)
590-595
: Clarify test name for missing paramMinor: rename to be explicit about which param is missing.
-@pytest.mark.asyncio -async def test_direct_api_call_missing_param(client: AsyncClient): +@pytest.mark.asyncio +async def test_direct_api_call_missing_endpoint_path(client: AsyncClient):
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
tests/api/test_routes.py
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/000-role-and-task.mdc)
**/*.py
: The MCP server must be implemented in Python, as you are a senior Python developer and the expertise is in Python.
The MCP server must wrap Blockscout APIs and expose blockchain data (balances, tokens, NFTs, contract metadata) via the Model Context Protocol (MCP).
The MCP server must communicate with AI agents/chat applications through stdin.
**/*.py
: Regular Python modules should generally not exceed 500 lines of code (LOC). If a module approaches this limit, consider splitting it into multiple focused modules (e.g., address_tools.py and address_tools_advanced.py) to maintain readability and logical organization.
ALL import statements must be placed at the top of the Python module, immediately after the module docstring (if present) and before any other code. Never insert imports inline near where the functionality is used. Follow PEP 8 import order.
ALL linting and formatting issues must be resolved before committing or pushing code. Use the Ruff rules defined in 300-ruff-lint-and-format.mdc to identify and fix issues.
**/*.py
: Always runruff check . --fix
andruff format .
on generated code before suggesting commits or opening a PR
Avoid using# noqa: E501
for ordinary code lines; split long lines instead. Only use# noqa: E501
for docstrings or string literals that must exceed 120 characters.
Use Ruff to enforce a 120-character line length, compatible with Black formatting
Files:
tests/api/test_routes.py
tests/api/test_routes.py
📄 CodeRabbit inference engine (.cursor/rules/200-development-testing-workflow.mdc)
tests/api/test_routes.py
: Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
tests/api/test_routes.py
: Create aFastMCP
instance and register routes usingregister_api_routes
before making requests in tests for the REST API
Usehttpx.AsyncClient
withASGITransport
to call the routes in REST API route tests
Patch the wrapped tool functions withunittest.mock.patch
andAsyncMock
to avoid real network calls in REST API route tests
Assert that each endpoint returns the expected HTTP status and JSON content in REST API route tests
Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Include error-case tests to confirm a400
response is returned when required query parameters are missing in REST API route tests
Test the static endpoints (e.g./health
,/
,/llms.txt
) to ensure they return the correct status code and content type afterregister_api_routes
is called, and confirm these routes are unavailable on a cleanFastMCP
instance before registration
For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP400
Name the test functions after the endpoint without the_rest
suffix (e.g., usetest_get_chains_list_success
instead oftest_get_chains_list_rest_success
)
Files:
tests/api/test_routes.py
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to api/routes.py : Expose each new tool via the REST API by creating a wrapper endpoint in api/routes.py and registering the route under the /v1/ prefix.
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP `400`
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Patch the wrapped tool functions with `unittest.mock.patch` and `AsyncMock` to avoid real network calls in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Include error-case tests to confirm a `400` response is returned when required query parameters are missing in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Ensure all external API calls in tests are properly mocked using unittest.mock.patch and AsyncMock.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-29T04:02:27.836Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to blockscout_mcp_server/tools/*.py : For paginated tools, accept an optional cursor argument and use apply_cursor_to_params to handle incoming cursors.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/test_*_integration.py : Integration tests for tools supporting cursor-based pagination must perform a two-step test: first call without a cursor, extract the cursor, then call again with the cursor and assert the second page's data is different from the first.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Assert that mocked API helper functions (such as make_blockscout_request) are called exactly once with the correct api_path and params in tests.
Applied to files:
tests/api/test_routes.py
🧬 Code graph analysis (1)
tests/api/test_routes.py (1)
blockscout_mcp_server/models.py (1)
ToolResponse
(375-400)
🔇 Additional comments (1)
tests/api/test_routes.py (1)
558-571
: Direct API happy-path test — LGTMCovers required params only and asserts correct forwarding to the tool. No issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
API.md (2)
491-495
: Document cursor-only pagination; call out that limit/page_size are ignored.This endpoint’s behavior (and this PR’s intent) will be clearer if we explicitly say that page sizing is server-defined and any limit-like params are ignored.
-Allows calling a curated raw Blockscout API endpoint for advanced or chain-specific data. +Allows calling a curated raw Blockscout API endpoint for advanced or chain-specific data. + +Note: Pagination is cursor-based. The server applies a chain-specific default page size; limit or page_size +parameters are ignored. Use the `cursor` from the previous response's `pagination.next_call.params` to +fetch subsequent pages.
497-506
: Fix markdownlint MD036 by using subheadings instead of bold.Switch “Parameters” and “Example Request” to proper headings to satisfy the linter without altering content.
-**Parameters** +#### Parameters @@ -**Example Request** +#### Example Request
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
API.md
(1 hunks)tests/api/test_routes.py
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/000-role-and-task.mdc)
**/*.py
: The MCP server must be implemented in Python, as you are a senior Python developer and the expertise is in Python.
The MCP server must wrap Blockscout APIs and expose blockchain data (balances, tokens, NFTs, contract metadata) via the Model Context Protocol (MCP).
The MCP server must communicate with AI agents/chat applications through stdin.
**/*.py
: Regular Python modules should generally not exceed 500 lines of code (LOC). If a module approaches this limit, consider splitting it into multiple focused modules (e.g., address_tools.py and address_tools_advanced.py) to maintain readability and logical organization.
ALL import statements must be placed at the top of the Python module, immediately after the module docstring (if present) and before any other code. Never insert imports inline near where the functionality is used. Follow PEP 8 import order.
ALL linting and formatting issues must be resolved before committing or pushing code. Use the Ruff rules defined in 300-ruff-lint-and-format.mdc to identify and fix issues.
**/*.py
: Always runruff check . --fix
andruff format .
on generated code before suggesting commits or opening a PR
Avoid using# noqa: E501
for ordinary code lines; split long lines instead. Only use# noqa: E501
for docstrings or string literals that must exceed 120 characters.
Use Ruff to enforce a 120-character line length, compatible with Black formatting
Files:
tests/api/test_routes.py
tests/api/test_routes.py
📄 CodeRabbit inference engine (.cursor/rules/200-development-testing-workflow.mdc)
tests/api/test_routes.py
: Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
tests/api/test_routes.py
: Create aFastMCP
instance and register routes usingregister_api_routes
before making requests in tests for the REST API
Usehttpx.AsyncClient
withASGITransport
to call the routes in REST API route tests
Patch the wrapped tool functions withunittest.mock.patch
andAsyncMock
to avoid real network calls in REST API route tests
Assert that each endpoint returns the expected HTTP status and JSON content in REST API route tests
Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Include error-case tests to confirm a400
response is returned when required query parameters are missing in REST API route tests
Test the static endpoints (e.g./health
,/
,/llms.txt
) to ensure they return the correct status code and content type afterregister_api_routes
is called, and confirm these routes are unavailable on a cleanFastMCP
instance before registration
For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP400
Name the test functions after the endpoint without the_rest
suffix (e.g., usetest_get_chains_list_success
instead oftest_get_chains_list_rest_success
)
Files:
tests/api/test_routes.py
API.md
📄 CodeRabbit inference engine (.cursor/rules/800-api-documentation-guidelines.mdc)
API.md
: Whenever a new MCP tool is added or an existing one is modified, its corresponding REST API endpoint in API.md MUST be added or updated.
Each endpoint documentation MUST follow the exact Markdown structure specified for consistency, including the heading format, parameter table, and example request.
The heading should be the human-readable tool name, with the function name in backticks.
The parameter table must clearly distinguish between required and optional parameters.
The curl example should demonstrate a realistic use case, including optional parameters where applicable.Update REST API documentation in API.md for each new or updated endpoint, following the API documentation guidelines.
Files:
API.md
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to api/routes.py : Expose each new tool via the REST API by creating a wrapper endpoint in api/routes.py and registering the route under the /v1/ prefix.
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : When editing REST API tests, follow the guidelines in 230-api-route-tests.mdc
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-29T04:02:27.836Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to blockscout_mcp_server/tools/*.py : For paginated tools, accept an optional cursor argument and use apply_cursor_to_params to handle incoming cursors.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/test_*_integration.py : Integration tests for tools supporting cursor-based pagination must perform a two-step test: first call without a cursor, extract the cursor, then call again with the cursor and assert the second page's data is different from the first.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Include error-case tests to confirm a `400` response is returned when required query parameters are missing in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : For each tool-based endpoint, create three tests: (1) success path with only required parameters, (2) success path including optional parameters if any, (3) failure path when a required parameter is missing, expecting HTTP `400`
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Patch the wrapped tool functions with `unittest.mock.patch` and `AsyncMock` to avoid real network calls in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Ensure all external API calls in tests are properly mocked using unittest.mock.patch and AsyncMock.
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Verify that the patched tool was invoked exactly once with the expected arguments in REST API route tests
Applied to files:
tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Assert that mocked API helper functions (such as make_blockscout_request) are called exactly once with the correct api_path and params in tests.
Applied to files:
tests/api/test_routes.py
🧬 Code graph analysis (1)
tests/api/test_routes.py (1)
blockscout_mcp_server/models.py (1)
ToolResponse
(375-400)
🪛 markdownlint-cli2 (0.17.2)
API.md
497-497: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
506-506: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🔇 Additional comments (1)
tests/api/test_routes.py (1)
558-597
: LGTM: Route wrapper tests cover required, optional (query_params + cursor), and 400 cases.
- Correct patch target (
blockscout_mcp_server.api.routes.direct_api_call
)- Verifies forwarded args (including dict decoding for
query_params
) and single invocation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
API.md (2)
497-497
: Use headings instead of bold for “Parameters” and “Example Request” (fix MD036).Swap bold text for H4 headings to satisfy markdownlint and keep structure consistent.
-**Parameters** +#### Parameters @@ -**Example Request** +#### Example RequestAlso applies to: 506-506
491-510
: Minor doc polish: reinforce “no query in endpoint_path”.Add a short note right under the section header to reiterate that endpoint_path must not include a query string (aligns with tool validation).
### Direct API Call (`direct_api_call`) -Allows calling a curated raw Blockscout API endpoint for advanced or chain-specific data. +Allows calling a curated raw Blockscout API endpoint for advanced or chain-specific data. +Note: Do not include query strings in `endpoint_path`; pass all parameters via `query_params[...]`.TESTING.md (1)
230-235
: Add a second-step pagination example.Show a follow-up curl using the returned cursor to demonstrate the paging workflow end-to-end.
AGENTS.md (1)
286-288
: Parameter naming inconsistency with API: usetransaction_hash
(nothash
).Aligns with
API.md
and tool signature used across docs.- * `transaction_tools.py`: Implements `get_transactions_by_address(chain_id, address, age_from, age_to, methods, cursor=None)`, `get_token_transfers_by_address(chain_id, address, age_from, age_to, token, cursor=None)`, `get_transaction_info(chain_id, hash, include_raw_input=False)`, `transaction_summary(chain_id, hash)`, `get_transaction_logs(chain_id, hash, cursor=None)`, etc. + * `transaction_tools.py`: Implements `get_transactions_by_address(chain_id, address, age_from, age_to, methods, cursor=None)`, `get_token_transfers_by_address(chain_id, address, age_from, age_to, token, cursor=None)`, `get_transaction_info(chain_id, transaction_hash, include_raw_input=False)`, `transaction_summary(chain_id, transaction_hash)`, `get_transaction_logs(chain_id, transaction_hash, cursor=None)`, etc.tests/tools/test_direct_api_tools.py (3)
37-37
: Preferawait_count
for AsyncMock assertions.Assert awaited calls, not just invocations, for tighter guarantees and consistency with repo tests.
- assert mock_ctx.report_progress.call_count == 3 + assert mock_ctx.report_progress.await_count == 3 @@ - assert mock_ctx.report_progress.call_count == 3 + assert mock_ctx.report_progress.await_count == 3 @@ - assert mock_ctx.report_progress.call_count == 3 + assert mock_ctx.report_progress.await_count == 3 @@ - assert mock_ctx.report_progress.call_count == 2 + assert mock_ctx.report_progress.await_count == 2 @@ - assert mock_ctx.report_progress.call_count == 1 + assert mock_ctx.report_progress.await_count == 1Also applies to: 91-91, 126-126, 150-150, 173-173
154-167
: Unnecessary patch ofmake_blockscout_request
in the negative-path test.The function is intentionally not called; removing this patch reduces noise.
- with ( - patch( - "blockscout_mcp_server.tools.direct_api_tools.get_blockscout_base_url", - new_callable=AsyncMock, - ) as mock_get_url, - patch( - "blockscout_mcp_server.tools.direct_api_tools.make_blockscout_request", - new_callable=AsyncMock, - ) as mock_request, - ): + with patch( + "blockscout_mcp_server.tools.direct_api_tools.get_blockscout_base_url", + new_callable=AsyncMock, + ) as mock_get_url: @@ - mock_request.assert_not_called()
95-126
: Optional: add a test asserting query_params propagation into next_call when provided.Covers the branch where initial call includes query_params and ensures they’re retained in pagination.
I can draft this additional test if helpful.
blockscout_mcp_server/tools/transaction_tools.py (1)
548-555
: Make the instruction copy-pasteable (show full call signature)Small clarity win: present a direct function-call example including chain_id so users can paste/run without editing sentence fragments.
Apply this diff:
- instructions = [ - ( - "To check for ERC-4337 User Operations related to this tx, call " - f"`direct_api_call` with endpoint `/api/v2/proxy/account-abstraction/operations` " - f"with query_params={{'transaction_hash': '{transaction_hash}'}}." - ) - ] + instructions = [ + ( + "Check ERC-4337 User Operations for this tx with direct_api_call: " + "direct_api_call(chain_id, '/api/v2/proxy/account-abstraction/operations', " + f"{{'transaction_hash': '{transaction_hash}'}})" + ) + ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
AGENTS.md
(3 hunks)API.md
(1 hunks)TESTING.md
(1 hunks)blockscout_mcp_server/tools/direct_api_tools.py
(1 hunks)blockscout_mcp_server/tools/transaction_tools.py
(1 hunks)gpt/openapi.yaml
(1 hunks)tests/integration/test_direct_api_tools_integration.py
(1 hunks)tests/tools/test_direct_api_tools.py
(1 hunks)tests/tools/test_transaction_tools_2.py
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- tests/integration/test_direct_api_tools_integration.py
- tests/tools/test_transaction_tools_2.py
- blockscout_mcp_server/tools/direct_api_tools.py
- gpt/openapi.yaml
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/000-role-and-task.mdc)
**/*.py
: The MCP server must be implemented in Python, as you are a senior Python developer and the expertise is in Python.
The MCP server must wrap Blockscout APIs and expose blockchain data (balances, tokens, NFTs, contract metadata) via the Model Context Protocol (MCP).
The MCP server must communicate with AI agents/chat applications through stdin.
**/*.py
: Regular Python modules should generally not exceed 500 lines of code (LOC). If a module approaches this limit, consider splitting it into multiple focused modules (e.g., address_tools.py and address_tools_advanced.py) to maintain readability and logical organization.
ALL import statements must be placed at the top of the Python module, immediately after the module docstring (if present) and before any other code. Never insert imports inline near where the functionality is used. Follow PEP 8 import order.
ALL linting and formatting issues must be resolved before committing or pushing code. Use the Ruff rules defined in 300-ruff-lint-and-format.mdc to identify and fix issues.
**/*.py
: Always runruff check . --fix
andruff format .
on generated code before suggesting commits or opening a PR
Avoid using# noqa: E501
for ordinary code lines; split long lines instead. Only use# noqa: E501
for docstrings or string literals that must exceed 120 characters.
Use Ruff to enforce a 120-character line length, compatible with Black formatting
Files:
tests/tools/test_direct_api_tools.py
blockscout_mcp_server/tools/transaction_tools.py
tests/tools/test_*.py
📄 CodeRabbit inference engine (.cursor/rules/200-development-testing-workflow.mdc)
Create or update the appropriate unit test file when adding new functionality or modifying existing code: Tool functions in tests/tools/test_{tool_module}.py
Files:
tests/tools/test_direct_api_tools.py
tests/tools/*
📄 CodeRabbit inference engine (.cursor/rules/210-unit-testing-guidelines.mdc)
tests/tools/*
: Each unit test in tests/tools/* must be narrow and specific; a single test should verify one specific behavior or scenario. If a test covers multiple scenarios or input parameter groups, split it into separate tests.
Use themock_ctx
pytest fixture from tests/conftest.py for mocking the MCP Context object in tests; do not create manual MagicMock instances for the context within test functions.
When testing tools that return a ToolResponse object, do not parse JSON from string results in your test. Instead, mock the serialization function (json.dumps) if used internally, and make assertions on the structured ToolResponse object and its attributes.
When testing tools that transform a list of items, programmatically generate the expected_result from the mock_api_response to keep tests maintainable, while still documenting the transformation logic.
Always verify the number of calls to mock_ctx.report_progress in tests to ensure progress tracking is tested.
Assert that mocked API helper functions (such as make_blockscout_request) are called exactly once with the correct api_path and params in tests.
For tools using make_request_with_periodic_progress, mock the wrapper itself and assert that it was called with the correct arguments (request_function, request_args, etc.).
Unit test files in tests/tools/* must not exceed 500 lines of code (LOC). If a file approaches this limit, split tests into multiple files to maintain readability and focus.
Write tests covering success scenarios, error cases, and edge cases in unit test files.
Ensure all external API calls in tests are properly mocked using unittest.mock.patch and AsyncMock.
Group related tests using descriptive class names or clear function naming patterns.
Files:
tests/tools/test_direct_api_tools.py
AGENTS.md
📄 CodeRabbit inference engine (.cursor/rules/110-new-mcp-tool.mdc)
Update AGENTS.md to document new or modified tool modules, including updates to the directory tree and examples sections.
Files:
AGENTS.md
blockscout_mcp_server/tools/*.py
📄 CodeRabbit inference engine (.cursor/rules/110-new-mcp-tool.mdc)
blockscout_mcp_server/tools/*.py
: Create or modify a tool module file in blockscout_mcp_server/tools/ for each new tool, using @log_tool_invocation to decorate each tool function.
All tools MUST return a strongly-typed ToolResponse[YourDataModel] instead of generic ToolResponse[dict].
For tools that query Blockscout API, use get_blockscout_base_url for dynamic chain resolution and make_blockscout_request for API calls.
For tools that use fixed API endpoints (like BENS), use the appropriate request helper (e.g., make_bens_request) from tools/common.py.
All tools MUST return a standardized ToolResponse[YourDataModel] object using the build_tool_response helper.
For paginated tools, accept an optional cursor argument and use apply_cursor_to_params to handle incoming cursors.
For paginated tools, use create_items_pagination from tools/common.py to handle slicing and pagination in responses.
For paginated tools, include the exact notice 'SUPPORTS PAGINATION: If response includes 'pagination' field, use the provided next_call to get additional pages.' in the tool docstring.
When returning addresses from Blockscout API responses, simplify address objects to a single address string in the tool output.
Truncate large data fields (such as raw 'data' or deeply nested values) in tool responses to save LLM context, and add notes about truncation.
Recursively truncate long strings in nested data structures in tool responses, replacing them with a structured object to signal truncation and adding notes.
Always raise exceptions for error conditions (e.g., ValueError, RuntimeError, TimeoutError) instead of returning ToolResponse objects with error messages in notes.
Use the report_and_log_progress helper from tools/common.py for all progress reporting in tool functions, instead of calling ctx.report_progress directly.
When making multiple independent API calls in a tool, use asyncio.gather with return_exceptions=True for concurrent execution and proper error handling.
Files:
blockscout_mcp_server/tools/transaction_tools.py
API.md
📄 CodeRabbit inference engine (.cursor/rules/800-api-documentation-guidelines.mdc)
API.md
: Whenever a new MCP tool is added or an existing one is modified, its corresponding REST API endpoint in API.md MUST be added or updated.
Each endpoint documentation MUST follow the exact Markdown structure specified for consistency, including the heading format, parameter table, and example request.
The heading should be the human-readable tool name, with the function name in backticks.
The parameter table must clearly distinguish between required and optional parameters.
The curl example should demonstrate a realistic use case, including optional parameters where applicable.Update REST API documentation in API.md for each new or updated endpoint, following the API documentation guidelines.
Files:
API.md
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to api/routes.py : Expose each new tool via the REST API by creating a wrapper endpoint in api/routes.py and registering the route under the /v1/ prefix.
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/api/test_routes.py : Create or update the appropriate unit test file when adding new functionality or modifying existing code: REST API endpoints in tests/api/test_routes.py
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/integration/test_*_integration.py : Add integration tests for tool functions in tests/integration/test_{tool_module}_integration.py when adding new tools that interact with live APIs
Applied to files:
tests/tools/test_direct_api_tools.py
AGENTS.md
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Ensure all external API calls in tests are properly mocked using unittest.mock.patch and AsyncMock.
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Always verify the number of calls to mock_ctx.report_progress in tests to ensure progress tracking is tested.
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : For tools using make_request_with_periodic_progress, mock the wrapper itself and assert that it was called with the correct arguments (request_function, request_args, etc.).
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-29T04:02:27.836Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to blockscout_mcp_server/tools/*.py : For paginated tools, accept an optional cursor argument and use apply_cursor_to_params to handle incoming cursors.
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/test_*_integration.py : Integration tests for tools supporting cursor-based pagination must perform a two-step test: first call without a cursor, extract the cursor, then call again with the cursor and assert the second page's data is different from the first.
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-22T00:14:13.016Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/230-api-route-tests.mdc:0-0
Timestamp: 2025-07-22T00:14:13.016Z
Learning: Applies to tests/api/test_routes.py : Patch the wrapped tool functions with `unittest.mock.patch` and `AsyncMock` to avoid real network calls in REST API route tests
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-22T00:13:40.792Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/210-unit-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:13:40.792Z
Learning: Applies to tests/tools/* : Assert that mocked API helper functions (such as make_blockscout_request) are called exactly once with the correct api_path and params in tests.
Applied to files:
tests/tools/test_direct_api_tools.py
📚 Learning: 2025-07-29T04:02:27.836Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to AGENTS.md : Update AGENTS.md to document new or modified tool modules, including updates to the directory tree and examples sections.
Applied to files:
AGENTS.md
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/test_*_integration.py : Tool-level integration tests targeting high-level MCP tool functions (e.g., get_latest_block, get_tokens_by_address) must be located in files matching tests/integration/test_*_integration.py and validate data extraction and schema against live API responses.
Applied to files:
AGENTS.md
📚 Learning: 2025-07-22T00:11:07.554Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/000-role-and-task.mdc:0-0
Timestamp: 2025-07-22T00:11:07.554Z
Learning: Applies to **/*.py : The MCP server must wrap Blockscout APIs and expose blockchain data (balances, tokens, NFTs, contract metadata) via the Model Context Protocol (MCP).
Applied to files:
AGENTS.md
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/*.py : Separate helper-level integration tests from tool-level integration tests into different files when appropriate.
Applied to files:
AGENTS.md
📚 Learning: 2025-07-22T00:14:04.976Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/220-integration-testing-guidelines.mdc:0-0
Timestamp: 2025-07-22T00:14:04.976Z
Learning: Applies to tests/integration/test_common_helpers.py : Helper-level integration tests targeting low-level helper functions in tools/common.py (e.g., make_blockscout_request, get_blockscout_base_url) must be located in tests/integration/test_common_helpers.py and verify basic network connectivity and response structure.
Applied to files:
AGENTS.md
📚 Learning: 2025-07-22T00:13:24.829Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/200-development-testing-workflow.mdc:0-0
Timestamp: 2025-07-22T00:13:24.829Z
Learning: Applies to tests/integration/test_common_helpers.py : Add integration tests for helper functions in tests/integration/test_common_helpers.py when modifying helper functions in tools/common.py
Applied to files:
AGENTS.md
📚 Learning: 2025-07-10T23:38:26.789Z
Learnt from: akolotov
PR: blockscout/mcp-server#145
File: API.md:170-176
Timestamp: 2025-07-10T23:38:26.789Z
Learning: When reviewing API parameter names, always verify the actual function signature in the source code rather than making assumptions. The get_transaction_info function in blockscout_mcp_server/tools/transaction_tools.py uses 'transaction_hash' as the parameter name, not 'hash'.
Applied to files:
blockscout_mcp_server/tools/transaction_tools.py
📚 Learning: 2025-07-29T04:02:27.836Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to API.md : Update REST API documentation in API.md for each new or updated endpoint, following the API documentation guidelines.
Applied to files:
TESTING.md
🧬 Code graph analysis (2)
tests/tools/test_direct_api_tools.py (3)
blockscout_mcp_server/models.py (2)
DirectApiData
(62-65)ToolResponse
(375-400)blockscout_mcp_server/tools/direct_api_tools.py (1)
direct_api_call
(19-87)tests/conftest.py (1)
mock_ctx
(8-13)
blockscout_mcp_server/tools/transaction_tools.py (1)
blockscout_mcp_server/tools/common.py (1)
build_tool_response
(489-530)
🪛 markdownlint-cli2 (0.17.2)
API.md
497-497: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
506-506: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Docker build and docker push
- GitHub Check: Run Integration Tests
🔇 Additional comments (3)
AGENTS.md (1)
38-39
: Good addition of direct_api_tools in structure.Accurately documents the new module and placement. No issues.
blockscout_mcp_server/tools/transaction_tools.py (2)
548-555
: Actionable follow-up via direct_api_call looks goodAdding contextual instructions that point to the AA operations endpoint is helpful and aligns with the acceptance criteria around suggesting follow-up endpoints.
548-555
: Confirm AA endpoint filter parameterThe current implementation instructs users to call
/api/v2/proxy/account-abstraction/operations
withquery_params={'transaction_hash': '<tx hash>'}
, but:
• API.md and TESTING.md examples only show filtering bysender
viaquery_params[sender]
, not by transaction hash.
• The documented path for fetching a specific User Operation is
/api/v2/proxy/account-abstraction/operations/{user_operation_hash}
,
suggesting the endpoint may not support atransaction_hash
query parameter.Please verify against your target Blockscout instances:
- Does
/api/v2/proxy/account-abstraction/operations
accept atransaction_hash
query parameter (versustx_hash
,hash
, or another name)?- If not, update the instructions to use the path parameter (e.g.
/operations/{user_operation_hash}
) or the correct query key.- Consider adding a test or example in TESTING.md to illustrate the supported filtering method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds a new direct_api_call
MCP tool that provides access to curated Blockscout API endpoints for specialized or chain-specific data not covered by existing tools. The implementation includes comprehensive testing, API documentation, and integration with the server's instructions system.
- Introduces
direct_api_call
tool with support for pagination and query parameters - Adds comprehensive unit and integration tests covering various usage scenarios
- Updates existing tools to provide contextual suggestions for using the direct API feature
Reviewed Changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
blockscout_mcp_server/tools/direct_api_tools.py |
New tool implementation with pagination support and parameter validation |
blockscout_mcp_server/models.py |
Added data models for direct API responses and endpoint curation |
blockscout_mcp_server/constants.py |
Added curated endpoint list and usage rules |
tests/tools/test_direct_api_tools.py |
Comprehensive unit tests for the new tool |
tests/integration/test_direct_api_tools_integration.py |
Integration tests including pagination scenarios |
Various configuration files | Version bumps and documentation updates |
There was a problem hiding this 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
🧹 Nitpick comments (4)
API.md (4)
497-505
: Fix markdownlint MD036: use headings instead of bold for “Parameters”.Convert the bold line to a subheading for consistency with linting.
-**Parameters** +#### Parameters @@ -| `query_params` | `object` | No | Additional query parameters forwarded to the Blockscout API. Use bracket syntax in the query string, e.g., `query_params[page]=1`. | +| `query_params` | `object` | No | Extra query parameters to forward upstream. Any query string key other than `chain_id`, `endpoint_path`, and `cursor` is forwarded automatically. Bracket syntax (e.g., `query_params[page]=1`) is also supported. |
506-510
: Fix markdownlint MD036: use heading for “Example Request” and show both param styles.Also add a second example including
cursor
.-**Example Request** +#### Example Request @@ -```bash -curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/proxy/account-abstraction/operations&query_params[sender]=0x91f51371D33e4E50e838057E8045265372f8d448" -``` +```bash +# Style 1: direct query keys (preferred) +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/proxy/account-abstraction/operations&sender=0x91f51371D33e4E50e838057E8045265372f8d448" + +# Style 2: bracket syntax (also supported) +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/proxy/account-abstraction/operations&query_params[sender]=0x91f51371D33e4E50e838057E8045265372f8d448" + +# Paginating with a cursor +curl "http://127.0.0.1:8000/v1/direct_api_call?chain_id=1&endpoint_path=/api/v2/proxy/account-abstraction/operations&cursor=eyJwYWdlIjoyfQ==" +```
495-505
: Add explicit pagination note for direct_api_call.State that
next_call.tool_name
will bedirect_api_call
and the cursor is opaque.`GET /v1/direct_api_call` -#### Parameters +> Pagination: When the response includes `pagination.next_call`, the `tool_name` will be `direct_api_call`. Pass the returned opaque `cursor` back to this endpoint unchanged. + +#### Parameters
503-505
: Document unsupportedlimit
behavior to prevent test/user confusion.Some Blockscout endpoints ignore or reject
limit
. Encourage cursor-based pagination and server defaults.| `cursor` | `string` | No | The cursor for pagination from a previous response. | +| — | — | — | Note: Many endpoints do not support arbitrary `limit` values; rely on server defaults and use `cursor` to paginate. |
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
API.md
(1 hunks)TESTING.md
(1 hunks)tests/integration/test_direct_api_tools_integration.py
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/integration/test_direct_api_tools_integration.py
- TESTING.md
🧰 Additional context used
📓 Path-based instructions (1)
API.md
📄 CodeRabbit inference engine (.cursor/rules/800-api-documentation-guidelines.mdc)
API.md
: Whenever a new MCP tool is added or an existing one is modified, its corresponding REST API endpoint in API.md MUST be added or updated.
Each endpoint documentation MUST follow the exact Markdown structure specified for consistency, including the heading format, parameter table, and example request.
The heading should be the human-readable tool name, with the function name in backticks.
The parameter table must clearly distinguish between required and optional parameters.
The curl example should demonstrate a realistic use case, including optional parameters where applicable.Update REST API documentation in API.md for each new or updated endpoint, following the API documentation guidelines.
Files:
API.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/110-new-mcp-tool.mdc:0-0
Timestamp: 2025-07-29T04:02:27.836Z
Learning: Applies to api/routes.py : Expose each new tool via the REST API by creating a wrapper endpoint in api/routes.py and registering the route under the /v1/ prefix.
🪛 markdownlint-cli2 (0.17.2)
API.md
497-497: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
506-506: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Docker build and docker push
- GitHub Check: Run Integration Tests
🔇 Additional comments (1)
API.md (1)
491-496
: Section title and route declaration look correct.Matches guideline: human-readable heading with function name in backticks and clear route path.
direct_api_call
tool
Summary
limit
query param from Arbitrum messages and blocks-validated integration testsTesting
ruff check .
ruff format --check .
pytest
pytest -m integration -v
(fails: contract call runtime errors)Closes #215
https://chatgpt.com/codex/tasks/task_b_68ae9cdda01c83238ea708381dfe2f3e
Summary by CodeRabbit
New Features
Documentation
Models
Tests
Chores