-
Notifications
You must be signed in to change notification settings - Fork 9
Introduction of tracking tool calls through mixpanel #199
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 opt-in Mixpanel analytics gated to HTTP mode + token, client metadata extraction utilities, REST request propagation into mock contexts with call_source, decorator wiring to emit analytics, config/env/Docker/packaging updates for Mixpanel, new tests, and bumps package version to 0.8.0-dev. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant REST_API as REST API
participant GetMock as get_mock_context
participant Decorator as @log_tool_invocation
participant Analytics
Client->>REST_API: HTTP request (tool endpoint)
REST_API->>GetMock: get_mock_context(request)
GetMock-->>REST_API: MockCtx(request_context, call_source="rest")
REST_API->>Decorator: invoke tool(ctx=MockCtx, args)
Decorator->>Analytics: track_tool_invocation(ctx, tool_name, args, client_meta)
Analytics-->>Decorator: event sent if enabled
Decorator-->>REST_API: tool result
REST_API-->>Client: HTTP response
sequenceDiagram
participant MCP_Client as MCP Client
participant MCP_Server as MCP Server
participant Decorator as @log_tool_invocation
participant Analytics
MCP_Client->>MCP_Server: MCP tool call (session.client_params)
MCP_Server->>Decorator: invoke tool(ctx, args)
Decorator->>Analytics: track_tool_invocation(ctx, tool_name, args, client_meta)
Analytics-->>Decorator: no-op unless HTTP mode + token
Decorator-->>MCP_Server: tool result
MCP_Server-->>MCP_Client: MCP response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the "Integrations" page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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: 4
🔭 Outside diff range comments (4)
blockscout_mcp_server/api/dependencies.py (1)
35-38
: Type get_mock_context signatureAdd typing to the dependency provider and drop the ignore.
-def get_mock_context(request=None) -> MockCtx: # type: ignore[no-untyped-def] +def get_mock_context(request: Optional["Request"] = None) -> MockCtx: """Dependency provider to get a mock context for stateless REST calls.""" return MockCtx(request=request)blockscout_mcp_server/server.py (2)
125-127
: Move import to module top (imports must be at the top per guidelines)Inline import inside main_command violates the repo guideline “ALL import statements must be placed at the top of the Python module”. Move it to the top and keep the call conditional.
- from blockscout_mcp_server.api.routes import register_api_routes - register_api_routes(mcp)Add at the top with other imports:
+from blockscout_mcp_server.api.routes import register_api_routes
Potential circular import: if this triggers a cycle, consider moving route registration helpers to a small module that doesn’t import server.py.
83-83
: Remove noqa E501 on a comment; wrap the line insteadGuideline: avoid # noqa: E501 on ordinary code lines. Break the long TODO into multiple lines.
-# TODO: structured_output is disabled for all tools so far to preserve the LLM context since it adds to the `list/tools` response ~20K tokens. # noqa: E501 +# TODO: structured_output is disabled for all tools so far to preserve the LLM context. +# It adds ~20K tokens to the `list/tools` response.tests/test_analytics.py (1)
49-75
: Assert analytics 'source' property and add a REST-source testtrack_tool_invocation populates a "source" property via _determine_call_source. Assert it here, and consider adding a dedicated test for the "rest" marker.
Apply this diff to extend the current test and add a new one:
def test_tracks_with_headers(monkeypatch): @@ - assert kwargs.get("meta") == {"ip": "203.0.113.5"} + assert kwargs.get("meta") == {"ip": "203.0.113.5"} + assert args[2]["source"] == "mcp" + + +def test_tracks_sets_source_rest_when_marker_present(monkeypatch): + monkeypatch.setattr(server_config, "mixpanel_token", "test-token", raising=False) + headers = {"x-forwarded-for": "198.51.100.1", "user-agent": "pytest-UA"} + req = DummyRequest(headers=headers) + ctx = DummyCtx(request=req, client_name="clientA", client_version="1.0.0") + ctx.call_source = "rest" + with patch("blockscout_mcp_server.analytics.Mixpanel") as mp_cls: + mp_instance = MagicMock() + mp_cls.return_value = mp_instance + analytics.set_http_mode(True) + analytics.track_tool_invocation( + ctx, + "tool_rest", + {"y": 3}, + client_meta=ClientMeta(name="clientA", version="1.0.0", protocol="2024-11-05", user_agent="pytest-UA"), + ) + args, kwargs = mp_instance.track.call_args + assert args[1] == "tool_rest" + assert args[2]["source"] == "rest"
🧹 Nitpick comments (16)
.env.example (1)
39-43
: Tighten dotenv style for new keys (ordering and quotes).
To satisfy dotenv linters and keep consistency: place API_HOST before TOKEN and avoid quotes for empty values.Apply this diff:
-# Optional Mixpanel analytics (HTTP mode only). Set token to enable; leave empty to disable. -# Use API host for regional endpoints (e.g., EU). No tracking occurs in stdio mode. -BLOCKSCOUT_MIXPANEL_TOKEN="" -BLOCKSCOUT_MIXPANEL_API_HOST="" +# Optional Mixpanel analytics (HTTP mode only). Set token to enable; leave empty to disable. +# Use API host for regional endpoints (e.g., EU). No tracking occurs in stdio mode. +BLOCKSCOUT_MIXPANEL_API_HOST= +BLOCKSCOUT_MIXPANEL_TOKEN=blockscout_mcp_server/config.py (1)
5-7
: Model config via SettingsConfigDict looks correctSwitch to SettingsConfigDict with env_prefix and .env support is appropriate. Consider adding env_file_encoding to be explicit.
- model_config = SettingsConfigDict(env_prefix="BLOCKSCOUT_", env_file=".env") + model_config = SettingsConfigDict(env_prefix="BLOCKSCOUT_", env_file=".env", env_file_encoding="utf-8")AGENTS.md (2)
22-24
: Project tree updated — also add env variables to docsSince analytics/client_meta were added, please also add BLOCKSCOUT_MIXPANEL_TOKEN and BLOCKSCOUT_MIXPANEL_API_HOST to the environment variables section below for discoverability.
Proposed addition under the “.env.example” list:
- BLOCKSCOUT_MIXPANEL_TOKEN: Mixpanel project token to enable analytics (HTTP mode only). Leave empty to disable.
- BLOCKSCOUT_MIXPANEL_API_HOST: Optional custom API host (e.g., api-eu.mixpanel.com).
211-222
: Clarify analytics opt-in behavior and PII considerationsDocument explicitly that analytics:
- Are emitted only in HTTP mode and only when BLOCKSCOUT_MIXPANEL_TOKEN is set.
- Include client IP (from headers) and user-agent; operators should ensure this aligns with their privacy policy.
I can draft a short “Privacy considerations” subsection if helpful.
blockscout_mcp_server/client_meta.py (1)
21-61
: Harden header extraction and simplify case-insensitive lookupStarlette Headers are case-insensitive; you can rely on that and pass a default to get, avoiding a second lookup and an extra try/except layer.
- # Read User-Agent from HTTP request (if present) - try: - request = getattr(getattr(ctx, "request_context", None), "request", None) - if request is not None: - headers = request.headers or {} - ua = headers.get("user-agent") or headers.get("User-Agent") or "" - user_agent = ua - except Exception: # pragma: no cover - pass + # Read User-Agent from HTTP request (if present) + request = getattr(getattr(ctx, "request_context", None), "request", None) + if request is not None: + headers = request.headers or {} + user_agent = headers.get("user-agent", "") # case-insensitive on Starlette HeadersOptional: log at debug level on unexpected ctx shapes instead of broad swallowing to aid troubleshooting.
blockscout_mcp_server/server.py (1)
123-131
: Use logger instead of print for startup messagesStartup messages should go through the configured logger for consistency with logging_utils.
- print(f"Starting Blockscout MCP Server with REST API on {http_host}:{http_port}") + logger.info("Starting Blockscout MCP Server with REST API on %s:%s", http_host, http_port) @@ - print(f"Starting Blockscout MCP Server in HTTP Streamable mode on {http_host}:{http_port}") + logger.info("Starting Blockscout MCP Server in HTTP Streamable mode on %s:%s", http_host, http_port)Add near the imports:
import logging logger = logging.getLogger(__name__)tests/test_analytics_helpers.py (2)
14-27
: Add coverage for header precedence when both XFF and X-Real-IP are presentCurrent tests cover independent cases. Add a case where both headers are present to lock in precedence (XFF over X-Real-IP), and also verify case-insensitivity for header names.
Apply this diff to add two focused tests:
@@ def test_extract_request_ip_fallbacks(): @@ assert ip2 == "10.0.0.5" + + +def test_extract_request_ip_precedence_when_both_headers_present(): + headers = {"X-Forwarded-For": "198.51.100.10, 203.0.113.20", "X-Real-IP": "192.0.2.9"} + request = SimpleNamespace(headers=headers, client=SimpleNamespace(host="10.0.0.1")) + ctx = SimpleNamespace(request_context=SimpleNamespace(request=request)) + ip = _extract_request_ip(ctx) + # Prefer X-Forwarded-For, left-most IP + assert ip == "198.51.100.10" + + +def test_extract_request_ip_case_insensitive_headers(): + headers = {"X-Forwarded-For": "203.0.113.30"} + request = SimpleNamespace(headers=headers, client=SimpleNamespace(host="10.0.0.1")) + ctx = SimpleNamespace(request_context=SimpleNamespace(request=request)) + ip = _extract_request_ip(ctx) + assert ip == "203.0.113.30"
30-38
: Strengthen distinct_id variability checks across all componentsYou validate stability and a change on version. Add quick assertions for changes in IP and client_name too.
Apply this diff to extend the test:
def test_build_distinct_id_stable(): @@ - c = _build_distinct_id("1.2.3.4", "client", "1.1") - assert c != a + c = _build_distinct_id("1.2.3.4", "client", "1.1") + assert c != a + d = _build_distinct_id("5.6.7.8", "client", "1.0") + assert d != a + e = _build_distinct_id("1.2.3.4", "clientZ", "1.0") + assert e != atests/test_analytics_source.py (1)
17-21
: Clarify test intent and add edge-case for empty explicit markerThe function doesn’t inspect session to decide source; it defaults to "mcp" regardless. The comment suggests session presence influences the result, which could mislead future readers. Also, add an explicit-empty-marker test to lock in the fallback behavior.
Apply this diff to adjust the comment and add an edge-case test:
def test_determine_call_source_mcp_when_session_present(): - # Even with no explicit marker, presence of client_params should be treated as mcp + # No explicit marker still defaults to 'mcp' regardless of session presence session = SimpleNamespace(client_params=SimpleNamespace()) ctx = SimpleNamespace(session=session) assert _determine_call_source(ctx) == "mcp" + + +def test_determine_call_source_empty_string_defaults_to_mcp(): + ctx = SimpleNamespace(call_source="") + assert _determine_call_source(ctx) == "mcp"tests/tools/test_decorators.py (1)
58-83
: Reuse mock_ctx fixture for context instead of crafting a separate MagicMockKeeps tests uniform and aligns with guidelines for tests/tools. You can still inject the session on the fixture.
Apply this diff:
-async def test_log_tool_invocation_mcp_context(caplog: pytest.LogCaptureFixture) -> None: +async def test_log_tool_invocation_mcp_context(caplog: pytest.LogCaptureFixture, mock_ctx: Context) -> None: @@ - full_mock_ctx = MagicMock() - full_mock_ctx.session = mock_session - - await dummy_tool(1, ctx=full_mock_ctx) + mock_ctx.session = mock_session + await dummy_tool(1, ctx=mock_ctx)tests/test_client_meta.py (1)
30-39
: Add test for User-Agent fallback when client name is undefinedextract_client_meta_from_ctx falls back to User-Agent as client_name when clientInfo.name is missing. Add a test to lock this behavior in.
Apply this diff to add the test:
def test_extract_client_meta_partial(): @@ assert meta.protocol == UNKNOWN_PROTOCOL_VERSION + + +def test_extract_client_meta_uses_user_agent_when_name_missing(): + # No clientInfo; user agent present in HTTP request + headers = {"User-Agent": "ua-test/9.9.9"} + request = SimpleNamespace(headers=headers) + ctx = SimpleNamespace(request_context=SimpleNamespace(request=request)) + + meta = extract_client_meta_from_ctx(ctx) + assert meta.name == "ua-test/9.9.9" + assert meta.version == UNDEFINED_CLIENT_VERSION + assert meta.protocol == UNKNOWN_PROTOCOL_VERSIONtests/test_analytics.py (1)
24-32
: Avoid direct writes to a private module attribute when resetting stateDirectly mutating _mp_client works but couples tests to an internal detail. Prefer a small testing helper (e.g., analytics._reset_for_tests()) or, at minimum, using monkeypatch to set the attribute.
Apply this diff to utilize monkeypatch for setting the private attribute in the autouse fixture:
-@pytest.fixture(autouse=True) -def reset_mode_and_client(): - analytics.set_http_mode(False) - # Ensure private module state is reset between tests - analytics._mp_client = None # type: ignore[attr-defined] - yield - analytics.set_http_mode(False) - analytics._mp_client = None # type: ignore[attr-defined] +@pytest.fixture(autouse=True) +def reset_mode_and_client(monkeypatch): + analytics.set_http_mode(False) + # Ensure private module state is reset between tests + monkeypatch.setattr(analytics, "_mp_client", None, raising=False) # type: ignore[attr-defined] + yield + analytics.set_http_mode(False) + monkeypatch.setattr(analytics, "_mp_client", None, raising=False) # type: ignore[attr-defined]SPEC.md (1)
496-497
: Clarify the fallback behavior for client identification.The documentation states that the server falls back to the HTTP
User-Agent
header when the client name cannot be determined. Consider adding more context about when this fallback occurs and any limitations (e.g., User-Agent might not always be available in stdio mode).blockscout_mcp_server/analytics.py (2)
17-22
: Consider more specific exception handling for import errors.While the current approach handles missing Mixpanel gracefully, catching all exceptions might hide other import-related issues. Consider catching only
ImportError
specifically.try: # Import lazily; tests will mock this from mixpanel import Consumer, Mixpanel -except Exception: # pragma: no cover - import errors covered by no-op behavior in tests +except ImportError: # pragma: no cover - import errors covered by no-op behavior in tests Consumer = object # type: ignore[assignment] Mixpanel = object # type: ignore[assignment]
119-176
: Well-structured tracking function with proper gating and error handling.The function implements multiple layers of safety:
- HTTP mode gating
- Mixpanel client availability check
- Comprehensive exception handling
- Flexible metadata sourcing (passed or extracted)
The TODO comment on line 160 should be addressed after initial validation.
Would you like me to create an issue to track the removal of the temporary logging statement after Mixpanel analytics validation is complete?
blockscout_mcp_server/api/routes.py (1)
241-243
: Verify parameter usage in deprecated endpoint.The
get_address_logs_rest
function accepts arequest
parameter but doesn't use it since it immediately returns a deprecation response. While this is harmless, consider if the request should be used for logging deprecation attempts.Consider logging deprecation attempts for monitoring:
async def get_address_logs_rest(request: Request) -> Response: """REST wrapper for the get_address_logs tool. This endpoint is deprecated.""" + # Could log the deprecation attempt with client info from request return create_deprecation_response()
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.env.example
(1 hunks)AGENTS.md
(2 hunks)Dockerfile
(1 hunks)SPEC.md
(1 hunks)blockscout_mcp_server/__init__.py
(1 hunks)blockscout_mcp_server/analytics.py
(1 hunks)blockscout_mcp_server/api/dependencies.py
(2 hunks)blockscout_mcp_server/api/routes.py
(6 hunks)blockscout_mcp_server/client_meta.py
(1 hunks)blockscout_mcp_server/config.py
(2 hunks)blockscout_mcp_server/server.py
(2 hunks)blockscout_mcp_server/tools/decorators.py
(2 hunks)pyproject.toml
(2 hunks)pytest.ini
(1 hunks)tests/test_analytics.py
(1 hunks)tests/test_analytics_helpers.py
(1 hunks)tests/test_analytics_source.py
(1 hunks)tests/test_client_meta.py
(1 hunks)tests/tools/test_decorators.py
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*.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:
blockscout_mcp_server/api/dependencies.py
tests/test_analytics_source.py
tests/test_client_meta.py
blockscout_mcp_server/server.py
tests/test_analytics_helpers.py
blockscout_mcp_server/client_meta.py
blockscout_mcp_server/analytics.py
tests/test_analytics.py
tests/tools/test_decorators.py
blockscout_mcp_server/__init__.py
blockscout_mcp_server/config.py
blockscout_mcp_server/api/routes.py
blockscout_mcp_server/tools/decorators.py
{Dockerfile,docker-compose.yml}
📄 CodeRabbit Inference Engine (.cursor/rules/000-role-and-task.mdc)
The MCP server must be able to run locally or through Docker.
Files:
Dockerfile
Dockerfile
📄 CodeRabbit Inference Engine (.cursor/rules/110-new-mcp-tool.mdc)
Dockerfile
: Add new API environment variables to the Dockerfile when introducing a new external API.
For each new paginated tool, add the page size environment variable to the Dockerfile.
Files:
Dockerfile
tests/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: Other modules in tests/test_{module_name}.py
Files:
tests/test_analytics_source.py
tests/test_client_meta.py
tests/test_analytics_helpers.py
tests/test_analytics.py
.env.example
📄 CodeRabbit Inference Engine (.cursor/rules/110-new-mcp-tool.mdc)
.env.example
: Add new API environment variables to .env.example when introducing a new external API.
For each new paginated tool, add the page size environment variable to .env.example.
Files:
.env.example
blockscout_mcp_server/server.py
📄 CodeRabbit Inference Engine (.cursor/rules/110-new-mcp-tool.mdc)
Register each new tool in blockscout_mcp_server/server.py by importing the tool function and registering it with the MCP server using the @mcp.tool() decorator.
Files:
blockscout_mcp_server/server.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_decorators.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_decorators.py
pyproject.toml
📄 CodeRabbit Inference Engine (.cursor/rules/010-implementation-rules.mdc)
If any new package needs to be installed it needs to be done with 'sudo
which uv
pip install --system ""' command. Don't forget to update pyproject.toml with the new package.When updating the version of the MCP server, update the
version
field in the[project]
section ofpyproject.toml
.
Files:
pyproject.toml
blockscout_mcp_server/__init__.py
📄 CodeRabbit Inference Engine (.cursor/rules/130-version-management.mdc)
When updating the version of the MCP server, update the
__version__
variable inblockscout_mcp_server/__init__.py
.
Files:
blockscout_mcp_server/__init__.py
blockscout_mcp_server/config.py
📄 CodeRabbit Inference Engine (.cursor/rules/110-new-mcp-tool.mdc)
blockscout_mcp_server/config.py
: Add new API endpoint configuration variables (URL, timeout, API key) to the ServerConfig class in blockscout_mcp_server/config.py when introducing a new external API.
For each new paginated tool, add a dedicated page size configuration variable to blockscout_mcp_server/config.py.
Files:
blockscout_mcp_server/config.py
blockscout_mcp_server/api/routes.py
📄 CodeRabbit Inference Engine (.cursor/rules/150-rest-api-implementation.mdc)
blockscout_mcp_server/api/routes.py
: REST handler functions in api/routes.py MUST be named after the MCP tool they wrap, with an _rest suffix (e.g., get_block_info_rest).
All request parameter extraction and validation MUST be handled by the extract_and_validate_params helper from api/helpers.py.
All REST handlers MUST be decorated with the @handle_rest_errors decorator from api/helpers.py. Do not implement custom try...except blocks inside the handlers.
All REST API endpoints MUST be registered under the /v1/ path prefix in register_api_routes using the _add_v1_tool_route helper.When implementing API routes that serve static files (e.g.,
index.html
orllms.txt
), load these files once during module import or server startup and store their contents in module-level variables. Route handlers should return the cached data instead of reading from disk on each request. Provide fallback error handling if preloading fails so the server responds gracefully.
Files:
blockscout_mcp_server/api/routes.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/decorators.py
🧠 Learnings (20)
📚 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/* : Use the `mock_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.
Applied to files:
blockscout_mcp_server/api/dependencies.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 Dockerfile : Add new API environment variables to the Dockerfile when introducing a new external API.
Applied to files:
Dockerfile
📚 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/config.py : Add new API endpoint configuration variables (URL, timeout, API key) to the ServerConfig class in blockscout_mcp_server/config.py when introducing a new external API.
Applied to files:
Dockerfile
blockscout_mcp_server/config.py
📚 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:
Dockerfile
pyproject.toml
blockscout_mcp_server/__init__.py
SPEC.md
AGENTS.md
📚 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 .env.example : Add new API environment variables to .env.example when introducing a new external API.
Applied to files:
.env.example
📚 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:
tests/test_analytics_helpers.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/*.py : All integration test functions must be decorated with pytest.mark.integration.
Applied to files:
pytest.ini
📚 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 : 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.
Applied to files:
blockscout_mcp_server/analytics.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/tools/test_decorators.py
📚 Learning: 2025-07-22T00:12:19.870Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/130-version-management.mdc:0-0
Timestamp: 2025-07-22T00:12:19.870Z
Learning: Use the exact same version string in both `pyproject.toml` and `blockscout_mcp_server/__init__.py`.
Applied to files:
pyproject.toml
blockscout_mcp_server/__init__.py
📚 Learning: 2025-07-22T00:12:19.870Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/130-version-management.mdc:0-0
Timestamp: 2025-07-22T00:12:19.870Z
Learning: Applies to blockscout_mcp_server/__init__.py : When updating the version of the MCP server, update the `__version__` variable in `blockscout_mcp_server/__init__.py`.
Applied to files:
pyproject.toml
blockscout_mcp_server/__init__.py
📚 Learning: 2025-07-22T00:12:19.870Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/130-version-management.mdc:0-0
Timestamp: 2025-07-22T00:12:19.870Z
Learning: Applies to pyproject.toml : When updating the version of the MCP server, update the `version` field in the `[project]` section of `pyproject.toml`.
Applied to files:
pyproject.toml
blockscout_mcp_server/__init__.py
📚 Learning: 2025-07-22T00:12:19.870Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/130-version-management.mdc:0-0
Timestamp: 2025-07-22T00:12:19.870Z
Learning: Update both `pyproject.toml` and `blockscout_mcp_server/__init__.py` simultaneously to maintain consistency across the codebase.
Applied to files:
pyproject.toml
blockscout_mcp_server/__init__.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: Ensure your changes are well-tested by checking test coverage using pytest --cov=blockscout_mcp_server --cov-report=term-missing
Applied to files:
pyproject.toml
blockscout_mcp_server/__init__.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/models.py : When defining Pydantic models for external API responses, use ConfigDict(extra="allow") to permit additional fields.
Applied to files:
blockscout_mcp_server/config.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/server.py : Register each new tool in blockscout_mcp_server/server.py by importing the tool function and registering it with the MCP server using the mcp.tool() decorator.
Applied to files:
SPEC.md
📚 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 : All tools MUST return a standardized ToolResponse[YourDataModel] object using the build_tool_response helper.
Applied to files:
SPEC.md
📚 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 tools that query Blockscout API, use get_blockscout_base_url for dynamic chain resolution and make_blockscout_request for API calls.
Applied to files:
SPEC.md
📚 Learning: 2025-07-22T00:12:50.427Z
Learnt from: CR
PR: blockscout/mcp-server#0
File: .cursor/rules/150-rest-api-implementation.mdc:0-0
Timestamp: 2025-07-22T00:12:50.427Z
Learning: Applies to blockscout_mcp_server/api/routes.py : REST handler functions in api/routes.py MUST be named after the MCP tool they wrap, with an _rest suffix (e.g., get_block_info_rest).
Applied to files:
blockscout_mcp_server/api/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 tools that use fixed API endpoints (like BENS), use the appropriate request helper (e.g., make_bens_request) from tools/common.py.
Applied to files:
blockscout_mcp_server/api/routes.py
🧬 Code Graph Analysis (9)
tests/test_analytics_source.py (1)
blockscout_mcp_server/analytics.py (1)
_determine_call_source
(101-116)
tests/test_client_meta.py (1)
blockscout_mcp_server/client_meta.py (1)
extract_client_meta_from_ctx
(21-61)
blockscout_mcp_server/server.py (1)
blockscout_mcp_server/analytics.py (1)
set_http_mode
(34-47)
tests/test_analytics_helpers.py (1)
blockscout_mcp_server/analytics.py (2)
_build_distinct_id
(94-98)_extract_request_ip
(71-91)
blockscout_mcp_server/analytics.py (2)
blockscout_mcp_server/client_meta.py (2)
ClientMeta
(14-18)extract_client_meta_from_ctx
(21-61)blockscout_mcp_server/api/dependencies.py (1)
info
(26-28)
tests/test_analytics.py (2)
blockscout_mcp_server/client_meta.py (1)
ClientMeta
(14-18)blockscout_mcp_server/analytics.py (2)
set_http_mode
(34-47)track_tool_invocation
(119-175)
tests/tools/test_decorators.py (2)
blockscout_mcp_server/tools/decorators.py (1)
log_tool_invocation
(13-49)tests/conftest.py (1)
mock_ctx
(8-13)
blockscout_mcp_server/api/routes.py (1)
blockscout_mcp_server/api/dependencies.py (1)
get_mock_context
(35-37)
blockscout_mcp_server/tools/decorators.py (2)
blockscout_mcp_server/client_meta.py (1)
extract_client_meta_from_ctx
(21-61)blockscout_mcp_server/analytics.py (1)
track_tool_invocation
(119-175)
🪛 GitHub Check: Docker build and docker push
Dockerfile
[warning] 35-35: Sensitive data should not be used in the ARG or ENV commands
SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "BLOCKSCOUT_MIXPANEL_TOKEN")
More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 41-41: [QuoteCharacter] The value has quote characters (', ")
[warning] 42-42: [QuoteCharacter] The value has quote characters (', ")
[warning] 42-42: [UnorderedKey] The BLOCKSCOUT_MIXPANEL_API_HOST key should go before the BLOCKSCOUT_MIXPANEL_TOKEN key
🪛 LanguageTool
SPEC.md
[style] ~515-~515: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...available). - MCP client version. - MCP protocol version. - Tool arguments (c...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🔇 Additional comments (22)
blockscout_mcp_server/__init__.py (1)
3-3
: Version consistency confirmed
The version inblockscout_mcp_server/__init__.py
(0.8.0-dev
) matches theversion
field inpyproject.toml
. Ready to merge.pytest.ini (1)
3-3
: Pytest ignore path addition is fine.
Ignoring a temp directory during collection is reasonable and non-invasive.pyproject.toml (2)
3-3
: Project version updated appropriately.
Aligned with blockscout_mcp_server/init.py and PR scope.
15-16
: Mixpanel runtime dependency addition acknowledged.
Pinning Mixpanel is appropriate given the new analytics integration.blockscout_mcp_server/api/dependencies.py (1)
17-19
: Good: Exposes request_context for analyticsExposing request_context aligned with the intended analytics shape without coupling to Starlette types at runtime.
blockscout_mcp_server/config.py (1)
38-41
: Mixpanel config fields added; ensure documented and default handlingFields look fine; analytics should treat empty strings as disabled (as per analytics.set_http_mode). Ensure AGENTS.md and .env.example include:
- BLOCKSCOUT_MIXPANEL_TOKEN
- BLOCKSCOUT_MIXPANEL_API_HOST (optional, e.g., api-eu.mixpanel.com)
Do you want me to add the entries to AGENTS.md and .env.example in this PR?
blockscout_mcp_server/client_meta.py (1)
8-10
: Sensible sentinel defaultsUsing "N/A" and "Unknown" sentinels keeps logs/analytics consistent and easy to filter.
blockscout_mcp_server/server.py (1)
134-135
: Analytics enablement in HTTP mode looks correctGated enablement aligns with analytics.set_http_mode design and avoids side effects in stdio mode.
tests/test_analytics_helpers.py (1)
6-12
: LGTM: Correct precedence for X-Forwarded-For and left-most IP parsingThe test accurately verifies preference for X-Forwarded-For and correctly selects the left-most IP.
tests/test_analytics_source.py (2)
6-9
: LGTM: Explicit source marker honoredThe test correctly asserts that an explicit call_source of "rest" is returned as-is.
11-15
: LGTM: Default to MCP when no explicit markerMatches the current implementation which defaults to "mcp" in absence of a marker.
tests/test_client_meta.py (2)
11-20
: LGTM: Full metadata extraction happy-pathThe assertions match the extraction logic for name, version, and protocol.
22-28
: LGTM: Defaults when everything is missingCovers the baseline defaults for name/version/protocol.
tests/test_analytics.py (1)
7-8
: No action needed:ClientMeta
is correctly re-exported
blockscout_mcp_server/analytics.py
importsClientMeta
(andextract_client_meta_from_ctx
) fromclient_meta
on line 24, and no__all__
is defined to restrict exports. The test import will work as written.blockscout_mcp_server/tools/decorators.py (3)
7-8
: LGTM! Clean integration with the new analytics module.The imports are properly structured following PEP 8 conventions, with standard library imports first, followed by local imports. The new analytics functionality is cleanly integrated without breaking existing behavior.
24-28
: Good use of the centralized metadata extraction helper.Using the shared
extract_client_meta_from_ctx
helper ensures consistency across the codebase and eliminates code duplication. The metadata extraction is now properly encapsulated and reusable.
30-40
: Excellent defensive programming for analytics tracking.The try-except block ensures that analytics failures never interrupt tool execution, which is critical for maintaining reliability. The explicit pass of
client_meta
totrack_tool_invocation
avoids redundant metadata extraction.SPEC.md (1)
500-527
: Well-documented analytics implementation with clear opt-in requirements.The Mixpanel analytics section provides comprehensive documentation of the tracking behavior, including:
- Clear activation requirements (HTTP mode + token)
- Detailed property tracking
- Anonymous identity generation approach
- REST API integration support
The opt-in nature and HTTP-only restriction appropriately limits analytics to scenarios where it makes sense.
blockscout_mcp_server/analytics.py (3)
50-69
: Good singleton pattern implementation for Mixpanel client.The lazy initialization and singleton pattern ensure efficient resource usage. The function properly handles both custom and default API hosts, with appropriate error handling and logging.
71-92
: Comprehensive IP extraction with proper header precedence.The implementation correctly prioritizes proxy headers (
X-Forwarded-For
,X-Real-IP
) over direct client IP, following standard practices for applications behind proxies. The parsing ofX-Forwarded-For
to extract the leftmost IP is correct.
94-99
: No change required: correct use of uuid.NAMESPACE_URLThe function prefixes your fingerprint with a full URL and generates a UUIDv5 from that URL. According to RFC 4122,
NameSpace_URL
is the appropriate namespace for URL-based identifiers—NameSpace_DNS
applies only to raw DNS names. You can ignore the suggestion to switch touuid.NAMESPACE_DNS
.Likely an incorrect or invalid review comment.
blockscout_mcp_server/api/routes.py (1)
78-85
: LGTM! Consistent request context propagation.All REST wrapper functions now properly accept and forward the
request
parameter toget_mock_context()
, enabling analytics and metadata extraction for REST API calls. The implementation is consistent across all 18 updated handlers.
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 (1)
blockscout_mcp_server/analytics.py (1)
125-182
: Robust tracking implementation with fallback behavior.The main tracking function properly handles both provided and extracted client metadata, builds comprehensive event properties, and includes defensive error handling to prevent analytics failures from breaking tool functionality. The temporary logging on Line 167-172 will help with initial validation.
Consider adding a configuration flag to control the verbose logging on Lines 167-172, as this could become noisy in production:
# TODO: Remove this log after validating Mixpanel analytics end-to-end - logger.info( - "Mixpanel event prepared: distinct_id=%s tool=%s properties=%s", - distinct_id, - tool_name, - properties, - ) + if getattr(config, "mixpanel_debug_logging", False): + logger.info( + "Mixpanel event prepared: distinct_id=%s tool=%s properties=%s", + distinct_id, + tool_name, + properties, + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
blockscout_mcp_server/analytics.py
(1 hunks)blockscout_mcp_server/client_meta.py
(1 hunks)blockscout_mcp_server/config.py
(2 hunks)tests/test_analytics.py
(1 hunks)tests/test_analytics_helpers.py
(1 hunks)tests/test_analytics_source.py
(1 hunks)tests/test_client_meta.py
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- tests/test_analytics_source.py
- tests/test_analytics.py
- tests/test_client_meta.py
- tests/test_analytics_helpers.py
- blockscout_mcp_server/client_meta.py
- blockscout_mcp_server/config.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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:
blockscout_mcp_server/analytics.py
🧬 Code Graph Analysis (1)
blockscout_mcp_server/analytics.py (2)
blockscout_mcp_server/client_meta.py (3)
ClientMeta
(14-18)extract_client_meta_from_ctx
(43-79)get_header_case_insensitive
(21-40)blockscout_mcp_server/api/dependencies.py (1)
info
(33-35)
⏰ 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: Run Integration Tests
- GitHub Check: Docker build and docker push
🔇 Additional comments (7)
blockscout_mcp_server/analytics.py (7)
1-9
: Comprehensive module docstring with clear gating conditions.The module docstring clearly explains the tracking conditions and the deterministic distinct_id approach, which is helpful for understanding the analytics behavior.
17-22
: Proper handling of optional Mixpanel dependency.The lazy import with graceful fallback handles cases where Mixpanel is not installed, which is good for optional analytics functionality.
38-52
: HTTP mode gating implementation looks good.The function correctly initializes the Mixpanel client when HTTP mode is enabled and provides appropriate logging for both enabled and disabled states. The best-effort client initialization helps validate configuration at startup.
54-73
: Singleton pattern with proper error handling.The Mixpanel client singleton implementation correctly handles configuration and gracefully degrades on initialization failures without breaking the application flow.
75-98
: Comprehensive IP extraction following proxy standards.The IP extraction logic correctly prioritizes proxy headers (X-Forwarded-For, X-Real-IP) before falling back to direct client IP, following standard practices for applications behind load balancers or proxies.
100-105
: Deterministic ID generation using UUID5.The use of UUID5 with a namespace and composite fingerprint ensures consistent distinct_id generation across sessions while maintaining user privacy through deterministic hashing.
107-123
: Clear call source determination logic.The function correctly prioritizes explicit markers from REST contexts while defaulting to 'mcp' for standard MCP calls, providing good traceability of request origins.
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 introduces opt-in Mixpanel analytics tracking for MCP tool invocations in HTTP modes. The implementation adds anonymous user tracking based on stable client fingerprints, captures tool usage patterns, and provides comprehensive client metadata extraction capabilities.
- Analytics tracking is enabled only in HTTP modes (MCP-over-HTTP and REST) when
BLOCKSCOUT_MIXPANEL_TOKEN
is set - Anonymous stable client identification using IP, client name, and version fingerprints
- Comprehensive client metadata extraction with fallback to User-Agent headers
Reviewed Changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
pyproject.toml | Adds Mixpanel dependency and bumps version to 0.8.0-dev |
blockscout_mcp_server/analytics.py | Core analytics implementation with Mixpanel integration |
blockscout_mcp_server/client_meta.py | Shared client metadata extraction utilities |
blockscout_mcp_server/tools/decorators.py | Integrates analytics tracking into existing log decorator |
blockscout_mcp_server/server.py | Enables analytics in HTTP mode |
blockscout_mcp_server/config.py | Adds Mixpanel configuration settings |
blockscout_mcp_server/api/* | Updates REST API to pass request context for analytics |
tests/* | Comprehensive test coverage for analytics functionality |
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
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: 3
🧹 Nitpick comments (1)
README.md (1)
174-176
: Fix README example: require token contract + concrete output; confirm Kinto chain resolvabilityShort: the README prompt at README.md:174-176 is vague and grammatically awkward — require the token contract address and explicit output fields (tx hash, timestamp, amount, sender). I searched the repo and found "Kinto" only in README, so I cannot confirm the chain is resolvable via Chainscout/Blockscout.
Please update/verify:
- README.md:174-176 — clarify wording and add token contract + desired return fields.
Suggested diff:
- When the most recent reward distribution of Kinto token was made to the wallet - `0x7D467D99028199D99B1c91850C4dea0c82aDDF52` in Kinto chain? + When was the most recent transfer of the KINTO token (provide the token contract address) to the wallet + `0x7D467D99028199D99B1c91850C4dea0c82aDDF52` on the Kinto chain? Return tx hash, timestamp, amount, and sender.Action required from author:
- Confirm that "Kinto" is resolvable by your Chainscout/Blockscout configuration (provide chain_id or Blockscout URL), or replace with a known chain.
- Provide the KINTO token contract address to use in the example so the README can show a concrete query/output.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
README.md
(2 hunks)blockscout_mcp_server/analytics.py
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- blockscout_mcp_server/analytics.py
⏰ 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: Run Integration Tests
- GitHub Check: Docker build and docker push
🔇 Additional comments (1)
README.md (1)
160-162
: Specify explicit chain_id and expected transaction-summary fields (confirm Chainscout discovery)All tools require a chain_id. I searched the repo: "Redstone" appears only in README.md (line ~161) and there is no local mapping / RECOMMENDED_CHAINS entry for it, so I could not verify Chainscout discoverability from the repository. Please replace the ambiguous README example with an explicit chain_id and list the expected output fields.
Files to update:
- README.md (lines ~160-162) — replace the ambiguous prompt with an explicit chain_id and required fields.
- If Redstone should be supported, add/confirm its chain_id in your chain mappings (RECOMMENDED_CHAINS / constants) or ensure get_blockscout_base_url(chain_id) resolves it via Chainscout.
Suggested diff:
- Tell me more about the transaction `0xf8a55721f7e2dcf85690aaf81519f7bc820bc58a878fa5f81b12aef5ccda0efb` - on Redstone rollup. + Summarize transaction `0xf8a55721f7e2dcf85690aaf81519f7bc820bc58a878fa5f81b12aef5ccda0efb` on Redstone (chain_id=<CHAIN_ID>). + Include: status, from, to, method name, decoded input parameters, native value, gas used/fees, and token transfers.Please confirm the exact chain_id your server uses for Redstone and whether Chainscout exposes that chain_id so I can re-verify.
Closes #173
Summary by CodeRabbit
New Features
Documentation
Chores
Tests