Skip to content

Move from FastAPI_MCP to FastMCP#280

Merged
gkorland merged 8 commits intostagingfrom
fastmcp
Oct 8, 2025
Merged

Move from FastAPI_MCP to FastMCP#280
gkorland merged 8 commits intostagingfrom
fastmcp

Conversation

@gkorland
Copy link
Contributor

@gkorland gkorland commented Oct 8, 2025

Summary by CodeRabbit

  • New Features
    • MCP integration added to the API with a dedicated endpoint and an environment toggle to disable it.
  • Improvements
    • Graph and database endpoints now use unified core logic with more consistent error responses.
    • Streaming responses for queries and destructive operations.
    • Refined API documentation tags and security descriptions.
  • Changes
    • JSON/FILE graph loaders are temporarily unavailable (return Not Implemented).
  • Chores
    • Upgraded runtime to Python 3.12.
    • Updated core framework and tooling dependencies.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

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

The PR updates dependencies and Python version in Pipfile, integrates MCP via fastmcp in the app factory, refactors graph routes to delegate to core APIs with standardized error handling, adjusts a schema loader function to accept an optional prefix, and tags the database connect route for MCP tooling.

Changes

Cohort / File(s) Summary of changes
Dependency and tooling updates
Pipfile
Bumped fastapi, uvicorn, litellm, authlib; added fastmcp; updated dev deps (pytest, playwright, pytest-playwright, pytest-asyncio); set python_version to 3.12.
MCP integration and app composition
api/app_factory.py
Replaced fastapi_mcp with fastmcp; added MCPType and RouteMap; created MCP HTTP app mounted at /mcp; environment toggle DISABLE_MCP; updated OpenAPI tags/security definitions; adjusted router inclusion and mounting order.
Graph routes refactor to core APIs
api/routes/graphs.py
Rewrote endpoints to delegate to core functions (list/get/query/confirm/refresh/delete); standardized JSON responses and error mapping; removed in-file models/constants (ChatRequest, ConfirmRequest, MESSAGE_DELIMITER); stubbed loaders with 501.
Schema loader API tweak
api/core/schema_loader.py
Changed list_databases(user_id, general_prefix) to accept Optional[str] for general_prefix.
Route tagging for MCP tools
api/routes/database.py
Added tags=["mcp_tool"] to POST /database decorator.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Env as Environment
  participant App as FastAPI App
  participant FM as FastMCP
  participant MCP as MCP HTTP App (/mcp)

  Env->>App: Start process
  App->>App: Initialize base FastAPI and include routers
  Env-->>App: Read DISABLE_MCP
  alt MCP enabled
    App->>FM: FastMCP.from_fastapi(app, route_maps=[resources, templates, tools, exclude])
    FM-->>App: FastMCP instance
    App->>FM: build_http_app()
    FM-->>MCP: MCP ASGI app
    App->>App: mount("/mcp", MCP)
    App->>App: merge MCP routes with main app
  else MCP disabled
    App->>App: Skip MCP setup (log)
  end
  App-->>Env: Ready to serve
Loading
sequenceDiagram
  autonumber
  participant Client
  participant API as Graph Routes (api/routes/graphs.py)
  participant Core as Core Services

  Client->>API: GET /graphs
  API->>Core: list_databases(user_id, GENERAL_PREFIX?)
  Core-->>API: list[str] or error
  API-->>Client: 200 list or mapped error

  Client->>API: GET /graphs/{id}
  API->>Core: get_schema(user_id, id)
  Core-->>API: schema or GraphNotFound/InternalError
  API-->>Client: 200/404/500

  Client->>API: POST /graphs/{id}/query
  API->>Core: query_database(user_id, id, chat_data)
  Core-->>API: streamed JSON or InvalidArgument
  API-->>Client: 200 stream or 400

  Client->>API: POST /graphs/{id}/confirm
  API->>Core: execute_destructive_operation(...)
  Core-->>API: streamed JSON or InvalidArgument
  API-->>Client: 200 stream or 400

  Client->>API: POST /graphs/{id}/refresh
  API->>Core: refresh_database_schema(...)
  Core-->>API: ok or InternalError
  API-->>Client: 200 or 500

  Client->>API: DELETE /graphs/{id}
  API->>Core: delete_database(...)
  Core-->>API: ok or InvalidArgument/GraphNotFound/InternalError
  API-->>Client: 200/400/404/500
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

Possibly related PRs

  • Staging #154 — Similar refactor in api/routes/graphs.py moving logic to centralized/core functions and aligning endpoint behaviors.
  • Add MCP server #153 — Introduces an MCP HTTP surface via app factory changes; closely related to this PR’s fastmcp-based integration.

Suggested reviewers

  • galshubeli

Poem

I hop through routes where graphs once sprawled,
Now cores reply when queries are called.
A burrow named /mcp I’ve spun,
With fastmcp, the tunnels run.
Dependencies fresh, the meadow bright—
Thump-thump! Deploying code tonight. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly summarizes the primary change by indicating the transition from the FastAPI_MCP integration to FastMCP, which aligns directly with the refactoring of imports and setup logic throughout the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.

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.

@gkorland gkorland changed the base branch from main to staging October 8, 2025 06:55
@github-actions
Copy link

github-actions bot commented Oct 8, 2025

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 4 package(s) with unknown licenses.
  • ⚠️ 1 packages with OpenSSF Scorecard issues.

View full job summary

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@gkorland
Copy link
Contributor Author

gkorland commented Oct 8, 2025

@CodeRabbit review

@gkorland gkorland requested a review from Copilot October 8, 2025 09:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

✅ Actions performed

Review triggered.

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

Copy link
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

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.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

Caution

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

⚠️ Outside diff range comments (3)
api/routes/graphs.py (1)

130-146: Add missing error handling for not-found and internal errors

Align with get_graph_data and delete_graph for consistent UX.

 async def query_graph(
     request: Request, graph_id: str, chat_data: ChatRequest
 ):  # pylint: disable=too-many-statements
@@
     try:
         generator = await query_database(request.state.user_id, graph_id, chat_data)
         return StreamingResponse(generator, media_type="application/json")
     except InvalidArgumentError as iae:
         return JSONResponse(content={"error": str(iae)}, status_code=400)
+    except GraphNotFoundError as gnfe:
+        return JSONResponse(content={"error": str(gnfe)}, status_code=404)
+    except InternalError as ie:
+        return JSONResponse(content={"error": str(ie)}, status_code=500)
api/routes/database.py (1)

28-35: Catch early URL validation errors; consider NDJSON content type

  • load_database can raise InvalidArgumentError before returning a generator; return 400 instead of 500.
  • The stream is delimiter-separated JSON; using application/x-ndjson can be clearer for clients.
-from api.core.schema_loader import load_database
+from api.core.schema_loader import load_database
+from api.core.errors import InvalidArgumentError
@@
-    generator = await load_database(db_request.url, request.state.user_id)
-    return StreamingResponse(generator, media_type="application/json")
+    try:
+        generator = await load_database(db_request.url, request.state.user_id)
+    except InvalidArgumentError as iae:
+        return JSONResponse(content={"error": str(iae)}, status_code=400)
+    # If you keep the custom delimiter, NDJSON is a clearer content type for clients parsing line-by-line.
+    return StreamingResponse(generator, media_type="application/x-ndjson")
Pipfile (1)

29-30: Regenerate Pipfile.lock to update its python_version to 3.12.

🧹 Nitpick comments (6)
api/routes/graphs.py (3)

83-86: Fix Ruff issues: unused request and File(None) default

  • Rename unused request to _request to satisfy ARG001.
  • Keep File(None) (FastAPI param marker) but silence Ruff B008 inline.

Apply:

-async def load_graph(
-    request: Request, data: GraphData = None, file: UploadFile = File(None)
-):  # pylint: disable=unused-argument
+async def load_graph(
+    _request: Request, data: GraphData | None = None, file: UploadFile | None = File(None)  # noqa: B008
+):  # pylint: disable=unused-argument

101-119: Harden extension detection

Handle case-insensitive extensions and empty filenames.

-        filename = file.filename
+        filename = (file.filename or "").lower()

168-180: Return 404 on missing graph during refresh

Add GraphNotFoundError handling.

     try:
         return await refresh_database_schema(request.state.user_id, graph_id)
-    except InternalError as ie:
+    except GraphNotFoundError as gnfe:
+        return JSONResponse(content={"error": str(gnfe)}, status_code=404)
+    except InternalError as ie:
         return JSONResponse(content={"error": str(ie)}, status_code=500)
api/app_factory.py (2)

168-176: Harden prod settings via env toggles

  • trusted_hosts="*" is permissive. Consider reading from an env var in prod.
  • Set SessionMiddleware https_only=True in staging/prod via env.
-    app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
+    trusted = os.getenv("TRUSTED_HOSTS", "*")
+    app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=trusted)
@@
-        https_only=False,  # True for HTTPS environments (staging/prod), False for HTTP dev
+        https_only=os.getenv("SESSION_COOKIE_SECURE", "false").lower() in ("1", "true", "yes"),

192-195: Silence Ruff ARG001: unused request param

Rename to _request.

-    async def handle_oauth_error(
-        request: Request, exc: Exception
-    ):  # pylint: disable=unused-argument
+    async def handle_oauth_error(
+        _request: Request, exc: Exception
+    ):  # pylint: disable=unused-argument
api/core/schema_loader.py (1)

144-163: Acknowledge intended namespace behavior; optional dedupe and sort

  • The mix of stripped user graphs and prefixed general graphs matches _graph_name logic and downstream expectations.
  • For stable ordering and to remove duplicates, consider returning sorted(set(filtered_graphs)).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63608c5 and 44c942e.

⛔ Files ignored due to path filters (1)
  • Pipfile.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • Pipfile (1 hunks)
  • api/app_factory.py (6 hunks)
  • api/core/schema_loader.py (2 hunks)
  • api/routes/database.py (1 hunks)
  • api/routes/graphs.py (7 hunks)
🧰 Additional context used
🪛 Ruff (0.13.3)
api/routes/graphs.py

84-84: Unused function argument: request

(ARG001)


84-84: Do not perform function call File in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

api/app_factory.py

193-193: Unused function argument: request

(ARG001)

⏰ 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: e2e-tests
🔇 Additional comments (5)
api/routes/graphs.py (2)

37-42: MCP tagging looks correct

Tagging the list route with "mcp_tool" aligns with the FastMCP route map.


193-202: Delete route handling is complete and consistent

Covers 400/404/500; good parity with other endpoints.

api/routes/database.py (1)

24-26: MCP tag added correctly

This makes the connect route discoverable as a tool.

Pipfile (1)

20-20: fastmcp addition aligns with app integration

Version pin looks good.

api/app_factory.py (1)

12-14: FastMCP imports and usage look correct

Good switch from FastAPI_MCP to FastMCP.

@gkorland gkorland merged commit 71bfe07 into staging Oct 8, 2025
9 checks passed
@gkorland gkorland deleted the fastmcp branch October 8, 2025 10:21
@coderabbitai coderabbitai bot mentioned this pull request Oct 8, 2025
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.

1 participant