Skip to content

Conversation

@GabrielBarberini
Copy link
Collaborator

@GabrielBarberini GabrielBarberini commented Oct 30, 2025

Summary by CodeRabbit

  • New Features

    • App now serves an MCP bridge at /mcp alongside existing REST endpoints and docs.
  • Documentation

    • Added comprehensive repository guidelines covering structure, commands, coding/testing standards, commit/PR guidance, and security/config tips.
    • Updated README with MCP Server usage notes.
  • Tests

    • Added MCP integration tests covering MCP build, route/tool export behavior, and combined app serving.
  • Chores

    • Updated dependencies: added fastmcp, removed motor, relaxed pymongo version constraint.
  • Refactor

    • Reworked app routing and repository initialization internals for separate REST and MCP apps.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 9860f5a and 64d8c80.

📒 Files selected for processing (1)
  • src/api.py (2 hunks)
 __________________________________________________________________________________________________________________________________________________________________________________________________________________
< Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test, it introduces security challenges, and it causes end-user and administrator frustration. - Ray Ozzie >
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Adds MCP integration mounted at /mcp with a cached FastMCP builder, refactors application wiring to expose rest_app and top-level app, replaces Motor with pymongo's AsyncMongoClient and per-instance idempotent init in the repository interface, updates dependencies, adds MCP tests, and introduces contributor documentation.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md, README.md
Add AGENTS.md with repository guidelines; update README.md describing MCP bridge mounted at /mcp.
Dependencies
requirements.txt
Remove motor; change to pymongo>=4.15; add fastmcp.
MCP integration & app wiring
src/mcp/server.py, src/api.py
New build_mcp(app: FastAPI) -> FastMCP with caching on app.state.mcp; introduce rest_app and top-level app, mount MCP at /mcp, adjust OpenAPI generation, middleware, instrumentation, and route/error handling to use rest_app.
Repository interface refactor
src/repositories/interface.py
Replace AsyncIOMotorClient with AsyncMongoClient; remove RepoInstances wrapper; adopt per-instance _init_lock, idempotent async init flow and updated event-loop scheduling; raise ConnectionError on connect failure; retain repository public method signatures.
Tests
tests/unit/test_mcp/test_mcp_server.py
New tests validating build_mcp caching, that FastAPI routes are exported as MCP tools (including path params), and that combined app serves REST and MCP endpoints.

Sequence Diagram(s)

sequenceDiagram
    participant Setup as App setup
    participant build_mcp as src/mcp/server.build_mcp
    participant FastMCP as FastMCP
    participant AppState as app.state
    participant HTTP as HTTP Server

    Note over Setup: During app creation, MCP is built and mounted
    Setup->>build_mcp: build_mcp(app)
    alt cached instance exists
        build_mcp-->>AppState: return app.state.mcp
    else first-time creation
        build_mcp->>FastMCP: FastMCP.from_fastapi(app, name=app.title)
        FastMCP-->>build_mcp: instance
        build_mcp->>AppState: store instance in app.state.mcp
    end
    build_mcp-->>Setup: FastMCP instance
    Setup->>HTTP: mount FastMCP at /mcp

    Note over HTTP: Request flow
    HTTP->>FastMCP: GET /mcp/tools
    FastMCP-->>HTTP: list tools (derived from FastAPI routes)
    HTTP->>FastMCP: POST /mcp/call
    FastMCP->>Setup: dispatch to `rest_app` route handler
    Setup-->>FastMCP: handler response
    FastMCP-->>HTTP: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • src/repositories/interface.pyAsyncMongoClient usage, connection lifecycle, per-instance _init_lock, event-loop scheduling and exception semantics.
    • src/mcp/server.py and src/api.py — caching on app.state, mounting MCP, OpenAPI parsing changes, and instrumentation placement.
    • requirements.txt — compatibility of pymongo>=4.15 and fastmcp with async runtime.
    • tests/unit/test_mcp/test_mcp_server.py — assumptions about route metadata and mocking of FastMCP.from_fastapi.

Suggested reviewers

  • aasitvora99
  • Gui-FernandesBR

Poem

🐇 A bridge at /mcp I gently laid,
Cached and ready, in the FastAPI glade.
Motor sleeps while PyMongo wakes,
Routes become tools with tiny shakes.
Hop, test, and ship — the rabbit celebrates.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "MCP server instrumentation and repositories interface cleaning" accurately describes the two main focal points of this changeset: the addition of MCP server integration (evidenced by src/mcp/server.py, modifications to src/api.py for MCP mounting, and new MCP tests) and the simplification of the repository interface (shown by the removal of RepoInstances, migration from AsyncIOMotorClient to AsyncMongoClient, and streamlined initialization in src/repositories/interface.py). The title is concise, specific, and avoids vague terminology while clearly communicating the primary changes from the developer's perspective, aligning well with the stated PR objectives.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a 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 integrates FastMCP server capabilities into the Infinity API and refactors the MongoDB repository layer to use pymongo's native async client. Key changes include:

  • Added FastMCP integration to automatically expose FastAPI routes as MCP tools
  • Migrated from motor library to pymongo>=4.15 with native AsyncMongoClient
  • Simplified repository singleton pattern by removing reference counting logic

Reviewed Changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/mcp/server.py New module providing FastMCP server builder with caching
tests/unit/test_mcp/test_mcp_server.py Test coverage for MCP server creation and route-to-tool mapping
src/api.py Mounts MCP server at /mcp endpoint and adds app title
src/repositories/interface.py Replaces motor with pymongo AsyncMongoClient, removes RepoInstances class and reference counting, simplifies locking
requirements.txt Removes motor, adds pymongo>=4.15 and fastmcp dependencies
README.md Documents the new MCP server endpoint
AGENTS.md New repository guidelines document for development conventions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/repositories/interface.py (1)

37-61: Initialization must finish before servicing operations

Creating a repository while an event loop is running (typical inside FastAPI handlers or startup hooks) now schedules _async_init() with loop.create_task(...), but no one awaits it before the first CRUD call. The very first insert/find/update immediately hits get_collection(), sees _initialized == False, and raises RepositoryNotInitializedException, turning the first request into an HTTP 503. Subsequent retries might work only if the background task finishes in time, so this is a deterministic correctness bug. Please gate repository methods on the pending init task (and keep a handle to propagate failures) so users never observe the transient uninitialized state.

@@
-    async def wrapper(self, *args, **kwargs):
+    async def wrapper(self, *args, **kwargs):
         try:
+            if not getattr(self, "_initialized", False):
+                init_task = getattr(self, "_init_task", None)
+                if init_task is not None:
+                    await init_task
+                else:
+                    await self._async_init()
             return await method(self, *args, **kwargs)
@@
-        if not getattr(self, '_initialized', False):
+        if not getattr(self, '_initialized', False):
             self.model = model
             self._max_pool_size = max_pool_size
             self._initialized_event = asyncio.Event()
+            self._init_task = None
             self._initialize()
@@
-        async with self._init_lock:
+        async with self._init_lock:
             if getattr(self, '_initialized', False):
                 return
 
             try:
                 self._initialize_connection()
             except Exception as e:
                 logger.error("Initialization failed: %s", e, exc_info=True)
                 self._initialized = False
+                self._init_task = None
                 raise
 
             self._initialized = True
             self._initialized_event.set()
+            self._init_task = None
@@
-        try:
+        try:
             loop = asyncio.get_running_loop()
         except RuntimeError:
             asyncio.run(self._async_init())
         else:
-            loop.create_task(self._async_init())
+            if getattr(self, "_init_task", None) is None:
+                self._init_task = loop.create_task(self._async_init())

Also applies to: 90-126

🧹 Nitpick comments (1)
tests/unit/test_mcp/test_mcp_server.py (1)

51-52: Consider documenting or making the naming convention more flexible.

The tool naming convention f"{route.name}_{tag}s" is hardcoded and assumes a specific pattern. If route naming conventions change, these tests will break. Consider either:

  • Documenting this convention in a comment
  • Moving the naming logic to a shared utility that both the MCP server and tests can reference
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 971f7ac and f18358b.

📒 Files selected for processing (7)
  • AGENTS.md (1 hunks)
  • README.md (1 hunks)
  • requirements.txt (2 hunks)
  • src/api.py (1 hunks)
  • src/mcp/server.py (1 hunks)
  • src/repositories/interface.py (5 hunks)
  • tests/unit/test_mcp/test_mcp_server.py (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-11-15T15:12:21.314Z
Learnt from: GabrielBarberini
PR: RocketPy-Team/Infinity-API#38
File: lib/routes/motor.py:75-75
Timestamp: 2024-11-15T15:12:21.314Z
Learning: When modifying API route paths in `lib/routes/motor.py`, changing from `/rocketpy/{motor_id}` to `/{motor_id}/rocketpy` is acceptable when no external consumers are affected. It's acceptable to introduce this breaking change if the team has decided to adopt the new approach, provided that the `README` and related documentation are updated accordingly.

Applied to files:

  • requirements.txt
📚 Learning: 2024-12-07T11:50:08.415Z
Learnt from: GabrielBarberini
PR: RocketPy-Team/Infinity-API#41
File: tests/test_routes/test_flights_route.py:122-125
Timestamp: 2024-12-07T11:50:08.415Z
Learning: In the project's pytest test files (e.g., `tests/test_routes/test_flights_route.py`), fixtures like `stub_rocket` and `stub_flight` are function-scoped and can be safely modified within tests without causing side effects.

Applied to files:

  • tests/unit/test_mcp/test_mcp_server.py
📚 Learning: 2024-06-10T19:08:40.396Z
Learnt from: GabrielBarberini
PR: RocketPy-Team/Infinity-API#20
File: lib/repositories/motor.py:19-27
Timestamp: 2024-06-10T19:08:40.396Z
Learning: The `Repositories` class is a singleton, which makes `MotorRepository` inherit this singleton behavior by transitivity.

Applied to files:

  • src/repositories/interface.py
🧬 Code graph analysis (3)
src/api.py (1)
src/mcp/server.py (1)
  • build_mcp (9-20)
tests/unit/test_mcp/test_mcp_server.py (1)
src/mcp/server.py (1)
  • build_mcp (9-20)
src/repositories/interface.py (1)
src/repositories/motor.py (3)
  • MotorRepository (9-36)
  • update_motor_by_id (29-32)
  • __init__ (17-18)
🪛 Ruff (0.14.2)
src/repositories/interface.py

83-83: Unused static method argument: args

(ARG004)


83-83: Unused static method argument: kwargs

(ARG004)


125-125: Store a reference to the return value of loop.create_task

(RUF006)

⏰ 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: Agent
🔇 Additional comments (3)
tests/unit/test_mcp/test_mcp_server.py (2)

15-21: LGTM: Good test isolation with autouse fixture.

The reset_mcp_state fixture properly ensures test isolation by cleaning up the MCP instance from app.state before and after each test. The use of autouse=True and defensive hasattr checks is appropriate.


24-35: LGTM: Well-structured test for MCP server creation and caching.

The test correctly validates both the initial creation via FastMCP.from_fastapi with the expected parameters and the caching behavior on subsequent calls.

AGENTS.md (1)

15-15: Documentation is accurate—79-character line length is correctly configured.

The project configuration confirms that both Black and Ruff are set to enforce 79-character line length in pyproject.toml, and the target version is Python 3.12. The AGENTS.md documentation statement is correct and requires no changes.

@GabrielBarberini
Copy link
Collaborator Author

@coderabbitai generate docstrings

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #62

coderabbitai bot added a commit that referenced this pull request Oct 30, 2025
Docstrings generation was requested by @GabrielBarberini.

* #61 (comment)

The following files were modified:

* `src/mcp/server.py`
* `src/repositories/interface.py`
* `tests/unit/test_mcp/test_mcp_server.py`
@GabrielBarberini
Copy link
Collaborator Author

Note

Docstrings generation - SUCCESS Generated docstrings for this pull request at #62

I think its worth this time since I remember to spend a good amount of time to develop the initial abstraction around repositories interface. Anyway this PR is just simplifying everything and giving up the scaffold to enable short living connections with mongo as long living seems to be very stable and initialization has a big overhead.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f18358b and 721b481.

📒 Files selected for processing (1)
  • src/mcp/server.py (1 hunks)
🔇 Additional comments (1)
src/mcp/server.py (1)

1-7: LGTM! Clean module structure.

The imports are minimal and appropriate for the MCP integration helper.

GabrielBarberini and others added 2 commits October 31, 2025 09:43
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Barberini <gabriel.barberini@sumup.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/api.py (1)

106-115: Consider adding type hints for clarity.

The _combine_lifespans function would benefit from type hints for its parameters and return type to improve code clarity and IDE support.

Apply this diff:

-def _combine_lifespans(rest_lifespan, mcp_lifespan):
+from collections.abc import AsyncGenerator
+from typing import Callable
+
+def _combine_lifespans(
+    rest_lifespan: Callable[[FastAPI], AsyncGenerator],
+    mcp_lifespan: Callable[[FastAPI], AsyncGenerator]
+) -> Callable[[FastAPI], AsyncGenerator]:
     """Combine FastAPI and MCP lifespans."""
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 721b481 and 6500475.

📒 Files selected for processing (3)
  • README.md (1 hunks)
  • src/api.py (2 hunks)
  • tests/unit/test_mcp/test_mcp_server.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • tests/unit/test_mcp/test_mcp_server.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/api.py (3)
src/__init__.py (1)
  • parse_error (22-25)
src/mcp/server.py (1)
  • build_mcp (9-21)
src/utils.py (1)
  • RocketPyGZipMiddleware (226-247)
🔇 Additional comments (4)
src/api.py (4)

24-41: LGTM! REST app refactoring is well-structured.

The separation of the REST application into rest_app is clean and consistent. All routers, instrumentation, and middleware are properly configured on the new instance.


44-72: LGTM! OpenAPI configuration updated correctly.

All references to app have been consistently updated to rest_app. The OpenAPI schema generation remains functionally unchanged.


75-99: LGTM! Routes migrated correctly with improved logging.

The routes and exception handlers are properly moved to rest_app. The logging change on line 96 is an improvement—using string formatting instead of f-strings for structured logging and error level for validation errors.


129-129: LGTM! Exports are appropriate.

Exporting both app and rest_app provides flexibility for consumers who may need direct access to either the combined application or just the REST portion.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (5)
src/mcp/server.py (2)

20-25: Potential race condition in caching pattern.

The check-then-set pattern (lines 20-25) could allow multiple FastMCP instances to be created if build_mcp is called concurrently before the first call completes. While this is likely not an issue if called only during single-threaded startup (as the mounting pattern in src/api.py suggests), consider documenting this constraint.

Add a note in the docstring:

 def build_mcp(app: FastAPI) -> FastMCP:
     """
     Create or return a cached FastMCP server that mirrors the given FastAPI app.
     
     Parameters:
         app (FastAPI): FastAPI application to mirror; the created FastMCP instance is cached on `app.state.mcp`.
     
     Returns:
         FastMCP: The FastMCP instance corresponding to the provided FastAPI app.
+    
+    Note:
+        This function should be called during single-threaded application startup.
+        Concurrent calls may result in multiple FastMCP instances being created.
     """

23-23: Document global settings mutation.

Line 23 permanently enables an experimental OpenAPI parser feature globally for the entire fastmcp library. This is a significant side effect that affects all fastmcp behavior in the application and is not obvious from the function signature.

Update the docstring to document this side effect:

 def build_mcp(app: FastAPI) -> FastMCP:
     """
     Create or return a cached FastMCP server that mirrors the given FastAPI app.
     
     Parameters:
         app (FastAPI): FastAPI application to mirror; the created FastMCP instance is cached on `app.state.mcp`.
     
     Returns:
         FastMCP: The FastMCP instance corresponding to the provided FastAPI app.
+    
+    Side Effects:
+        Enables `settings.experimental.enable_new_openapi_parser` globally, affecting
+        all fastmcp behavior in the process. This setting persists for the application lifetime.
     """
tests/unit/test_mcp/test_mcp_server.py (2)

64-64: Avoid using internal FastAPI API.

Accessing route.dependant.path_params relies on FastAPI internal implementation details that may change between versions. Consider using route.param_convertors.keys() for a more stable API to extract path parameters.

Apply this diff to use the public API:

     for tool_name, route in expected.items():
         schema = tool_by_name[tool_name].inputSchema or {}
         required = set(schema.get('required', []))
-        path_params = {param.name for param in route.dependant.path_params}
+        path_params = set(route.param_convertors.keys())
         assert path_params.issubset(
             required
         ), f"{tool_name} missing path params {path_params - required}"

50-55: Fix potential IndexError when accessing route.tags[0].

The code filters routes by include_in_schema but doesn't verify that route.tags is non-empty before accessing route.tags[0]. This will raise an IndexError if any route marked for schema inclusion has no tags.

Apply this diff to add defensive validation:

     expected = {}
     for route in rest_app.routes:
         if not isinstance(route, APIRoute) or not route.include_in_schema:
             continue
+        if not route.tags:
+            continue  # Skip routes without tags
         tag = route.tags[0].lower()
         tool_name = f"{route.name}_{tag}s"
         expected[tool_name] = route
src/repositories/interface.py (1)

138-139: Fix race condition in lock initialization.

Creating _init_lock with a check-then-create pattern outside synchronization allows multiple concurrent calls to _async_init() to create separate lock instances before any can acquire one, defeating the lock's purpose.

Move the lock initialization to __init__ to ensure a single lock instance per repository:

     def __init__(self, model: ApiBaseModel, *, max_pool_size: int = 3):
         """
         Initialize the repository instance for a specific API model and configure its connection pool.
         
         Parameters:
             model (ApiBaseModel): The API model used for validation and to determine the repository's collection.
             max_pool_size (int, optional): Maximum size of the MongoDB connection pool. Defaults to 3.
         
         Notes:
             If the instance is already initialized, this constructor will not reconfigure it. Initialization of the underlying connection is started asynchronously.
         """
         if not getattr(self, '_initialized', False):
             self.model = model
             self._max_pool_size = max_pool_size
             self._initialized_event = asyncio.Event()
+            self._init_lock = asyncio.Lock()
             self._initialize()

Then remove the hasattr check in _async_init:

     async def _async_init(self):
         """
         Perform idempotent, retry-safe asynchronous initialization of the repository instance.
         
         Ensures a per-instance asyncio.Lock exists and acquires it to run initialization exactly once; on success it marks the instance as initialized and sets the internal _initialized_event so awaiters can proceed. If initialization fails, the original exception from _initialize_connection is propagated after logging.
         """
         if getattr(self, '_initialized', False):
             return
 
-        if not hasattr(self, '_init_lock'):
-            self._init_lock = asyncio.Lock()
-
         async with self._init_lock:
             if getattr(self, '_initialized', False):
                 return
🧹 Nitpick comments (1)
src/repositories/interface.py (1)

155-166: Consider storing the task reference for observability.

The static analysis tool correctly flags that the create_task result is not stored. While the fire-and-forget pattern may be intentional for background initialization, storing the reference would allow tracking initialization failures and provide better observability.

Apply this diff to store and optionally track the initialization task:

     def _initialize(self):
         """
         Ensure the repository's asynchronous initializer is executed: run it immediately if no event loop is active, otherwise schedule it on the running loop.
         
         If there is no running asyncio event loop, this method runs self._async_init() to completion on the current thread, blocking until it finishes. If an event loop is running, it schedules self._async_init() as a background task on that loop and returns immediately.
         """
         try:
             loop = asyncio.get_running_loop()
         except RuntimeError:
             asyncio.run(self._async_init())
         else:
-            loop.create_task(self._async_init())
+            self._init_task = loop.create_task(self._async_init())
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6500475 and 326db29.

📒 Files selected for processing (3)
  • src/mcp/server.py (1 hunks)
  • src/repositories/interface.py (6 hunks)
  • tests/unit/test_mcp/test_mcp_server.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-16T00:03:14.224Z
Learnt from: GabrielBarberini
Repo: RocketPy-Team/Infinity-API PR: 38
File: tests/test_routes/test_motors_route.py:42-100
Timestamp: 2024-11-16T00:03:14.224Z
Learning: In `tests/test_routes/test_motors_route.py`, focus on assessing whether the API has the capability of validating the input schema, rather than testing the extension of Pydantic validations (e.g., testing empty request payloads).

Applied to files:

  • tests/unit/test_mcp/test_mcp_server.py
📚 Learning: 2024-06-10T19:08:40.396Z
Learnt from: GabrielBarberini
Repo: RocketPy-Team/Infinity-API PR: 20
File: lib/repositories/motor.py:19-27
Timestamp: 2024-06-10T19:08:40.396Z
Learning: The `Repositories` class is a singleton, which makes `MotorRepository` inherit this singleton behavior by transitivity.

Applied to files:

  • src/repositories/interface.py
🧬 Code graph analysis (3)
src/mcp/server.py (1)
src/api.py (1)
  • custom_openapi (31-56)
tests/unit/test_mcp/test_mcp_server.py (1)
src/mcp/server.py (1)
  • build_mcp (9-26)
src/repositories/interface.py (2)
src/models/interface.py (1)
  • ApiBaseModel (10-61)
src/secrets.py (2)
  • Secrets (5-21)
  • get_secret (17-21)
🪛 GitHub Actions: Pylint
src/mcp/server.py

[error] 26-26: pylint: C0304: Final newline missing (missing-final-newline)

tests/unit/test_mcp/test_mcp_server.py

[error] 84-84: pylint: C0304: Final newline missing (missing-final-newline)

src/repositories/interface.py

[error] 287-287: pylint: C0304: Final newline missing (missing-final-newline)

🪛 Ruff (0.14.2)
src/repositories/interface.py

96-96: Unused static method argument: args

(ARG004)


96-96: Unused static method argument: kwargs

(ARG004)


166-166: Store a reference to the return value of loop.create_task

(RUF006)

🔇 Additional comments (6)
src/repositories/interface.py (3)

37-76: LGTM!

The enhanced decorator provides comprehensive error handling with clear logging and standardized exception propagation.


96-109: LGTM!

The singleton pattern is correctly implemented with thread-safe instantiation. The static analysis warning about unused args and kwargs parameters is a false positive—these parameters are required by the __new__ signature for proper initialization coordination with __init__.


181-213: LGTM!

The migration from AsyncIOMotorClient to pymongo's AsyncMongoClient is correctly implemented with appropriate pool sizing, timeout configuration, and error handling.

tests/unit/test_mcp/test_mcp_server.py (3)

13-24: LGTM!

The fixture provides proper test isolation by clearing MCP state before and after each test.


27-37: LGTM!

The test correctly verifies both the initial FastMCP creation and the caching behavior on subsequent calls.


70-83: LGTM!

The test correctly verifies that REST endpoints remain functional alongside MCP integration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/api.py (1)

1-106: Add explicit __all__ export list for public API clarity.

The module exposes app and rest_app as public entities but lacks an explicit __all__ declaration. Adding this improves API clarity and prevents unintended exports.

Add this at the end of the file:

__all__ = ["app", "rest_app"]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2ddd2e and 9860f5a.

📒 Files selected for processing (1)
  • src/api.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/api.py (3)
src/__init__.py (1)
  • parse_error (22-25)
src/mcp/server.py (1)
  • build_mcp (9-26)
src/utils.py (1)
  • RocketPyGZipMiddleware (226-247)
🔇 Additional comments (5)
src/api.py (5)

1-14: LGTM! Clean import organization.

The imports are well-organized and all necessary for the new OpenTelemetry instrumentation and MCP server integration.


17-28: LGTM! Clean REST app initialization.

The separation of rest_app from the top-level app provides good modularity for mounting the MCP server alongside the REST API.


37-65: LGTM! OpenAPI customization correctly targets rest_app.

The custom OpenAPI function properly references rest_app for schema generation and caching, consistent with the new app structure.


69-80: LGTM! Route handlers correctly attached to rest_app.

Both the main page redirect and health check endpoint are properly configured on rest_app.


84-92: LGTM! Exception handler properly configured.

The validation exception handler is correctly attached to rest_app and uses appropriate error formatting.

@GabrielBarberini GabrielBarberini merged commit c7bb348 into master Nov 1, 2025
4 checks passed
@GabrielBarberini GabrielBarberini deleted the mcp_server branch November 1, 2025 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants