Skip to content

Conversation

@yczhang-nv
Copy link
Contributor

@yczhang-nv yczhang-nv commented Sep 26, 2025

Description

Closes AIQ-1943

By Submitting this PR I confirm:

  • I am familiar with the Contributing Guidelines.
  • We require that all contributors "sign-off" on their commits. This certifies that the contribution is your original work, or you have rights to submit it under the same license, or a compatible license.
    • Any contribution which contains commits that are not Signed-Off will not be accepted.
  • When the PR is ready for review, new or existing tests cover these changes.
  • When the PR is ready for review, the documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Added an MCP client tool-list HTTP endpoint showing per-group tool availability, session health, and workflow alignment.
    • CLI now awaits asynchronously produced function mappings when listing functions.
  • Bug Fixes

    • Added timeout handling when fetching MCP tools to avoid hangs and surface timeouts.
    • Skips invalid/missing tool functions and normalizes input schemas to prevent wiring failures.
  • Documentation

    • Added docs for the new HTTP tool-list endpoint with usage and sample output.
  • Tests

    • End-to-end tests for healthy/unhealthy MCP tool-list scenarios; updated exception expectations.

Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
@yczhang-nv yczhang-nv self-assigned this Sep 26, 2025
@yczhang-nv yczhang-nv requested a review from a team as a code owner September 26, 2025 02:14
@yczhang-nv yczhang-nv added feature request New feature or request non-breaking Non-breaking change labels Sep 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds anyio-based timeouts and MCP-specific exception handling to MCP client tool listing; introduces MCPFunctionGroup state and normalizes tool wiring; awaits async function mappings in the CLI; adds a FastAPI GET endpoint to list MCP client tools with tests and documentation updates.

Changes

Cohort / File(s) Summary of Changes
MCP client: timeout & exception handling
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
Adds top-level anyio import; decorates MCPBaseClient.get_tools with @mcp_exception_handler; wraps session.list_tools() in anyio.fail_after(...) and converts timeouts to MCPTimeoutError.
MCP client: function-group wiring & state
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
Adds MCPFunctionGroup(FunctionGroup) with mcp_client, mcp_client_server_name, mcp_client_transport properties; uses MCPFunctionGroup when building groups; captures live client details on the group; normalizes tool_fnsingle_fn (skips/logs None) and converts a type(None) sentinel to None for input_schema before calling group.add_function.
CLI: await async function mapping
src/nat/cli/commands/mcp/mcp.py
Awaits an awaitable fns mapping (adds await fns) before iterating .items(), changing when the function mapping is obtained.
FastAPI: MCP client tools HTTP endpoint
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
Adds add_mcp_client_tool_list_route(self, app: FastAPI, builder: WorkflowBuilder) in base and concrete worker; registers GET /mcp/client/tool/list to aggregate per-group MCP client tool info (session health, availability, configured overrides); imports HTTPException and Function as needed.
Docs: MCP client tools via HTTP
docs/source/workflows/mcp/mcp-client.md
Renames header and adds documentation for the new HTTP endpoint, usage steps, path/method, and sample JSON output.
Tests: FastAPI MCP client endpoint
tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
Adds tests and fixtures for /mcp/client/tool/list, covering a healthy session with alias/override and an unhealthy session marking tools unavailable.
Tests & exceptions: MCP connection error
tests/nat/mcp/test_mcp_client_base.py, packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py
Tests updated to assert MCPConnectionError instead of generic ConnectionError; MCPConnectionError added to MCP exceptions module.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller as Caller
  participant MCPClient as MCPBaseClient
  participant Session as MCP Session

  Caller->>MCPClient: get_tools()
  rect rgb(224,239,255)
    note right of MCPClient: anyio.fail_after(timeout)
    MCPClient->>Session: list_tools()
    alt completes before timeout
      Session-->>MCPClient: tools
      MCPClient-->>Caller: tools
    else timeout
      Session--x MCPClient: TimeoutError
      MCPClient-->>Caller: MCPTimeoutError(server_name)
    end
  end
Loading
sequenceDiagram
  autonumber
  participant Client as HTTP Client
  participant FastAPI as FastAPI App
  participant Worker as FastApiFrontEndPluginWorker
  participant Builder as WorkflowBuilder
  participant Group as FunctionGroup
  participant MCP as MCP Client

  Client->>FastAPI: GET /mcp/client/tool/list
  FastAPI->>Worker: handler(builder)
  Worker->>Builder: discover MCP client groups
  Builder-->>Worker: groups
  loop per group
    Worker->>Group: read mcp_client and server_name
    Worker->>MCP: get_tools() (async, timeout-wrapped)
    alt success
      MCP-->>Worker: server tools
      Worker->>Worker: cross-reference configured functions & overrides
    else error/timeout
      MCP-->>Worker: error
      Worker->>Worker: mark session unhealthy, tools unavailable
    end
  end
  Worker-->>FastAPI: MCPClientToolListResponse (JSON)
  FastAPI-->>Client: 200
Loading
sequenceDiagram
  autonumber
  participant CLI as CLI Command
  participant Fns as Awaitable[Mapping]

  CLI->>Fns: await fns
  Fns-->>CLI: mapping
  CLI->>CLI: iterate mapping.items()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 title clearly and concisely describes the primary change—adding the new mcp/client/tool/list endpoint—uses imperative mood, and stays well under the recommended length, matching the content of the pull request.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

Caution

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

⚠️ Outside diff range comments (1)
src/nat/cli/commands/mcp/mcp.py (1)

202-229: Await and reuse get_accessible_functions result
get_accessible_functions is async; await it once into a dict and use that mapping instead of calling .get on the coroutine or awaiting twice.

-        # Access functions exposed by the group
-        fns = group.get_accessible_functions()
+        # Access functions exposed by the group
+        fns_map = await group.get_accessible_functions()
@@
-            fn = fns.get(full)
+            fn = fns_map.get(full)
@@
-            for full, fn in (await fns).items():
+            for full, fn in fns_map.items():
🧹 Nitpick comments (5)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (1)

286-291: Log at error level when re-raising.

Since the exception is re-raised, prefer error over warning to align with our logging guidance.

As per coding guidelines

-        except Exception as e:
-            logger.warning("Failed to get tools: %s", e)
-            raise
+        except Exception as e:
+            logger.error("Failed to get tools: %s", e)
+            raise
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (3)

1092-1107: Add return type and remove duplicate imports inside method scope.

  • Add -> None to the public method signature.
  • Avoid re-importing Any and BaseModel inside the function; use existing module-level imports (typing.Any, BaseModel).

As per coding guidelines

-    async def add_mcp_client_tool_list_route(self, app: FastAPI, builder: WorkflowBuilder):
+    async def add_mcp_client_tool_list_route(self, app: FastAPI, builder: WorkflowBuilder) -> None:
-        from typing import Any
-
-        from pydantic import BaseModel
+        # Using module-level imports: typing.Any and BaseModel

         class MCPToolInfo(BaseModel):
             name: str
             description: str
             server: str
             available: bool

         class MCPClientToolListResponse(BaseModel):
-            mcp_clients: list[dict[str, Any]]
+            mcp_clients: list[dict[str, typing.Any]]

1199-1211: Log full stack traces when not re-raising.

You’re handling the error and returning fallback data; use logger.exception to retain stack trace context.

As per coding guidelines

-                        except Exception as e:
-                            logger.error(f"Error processing MCP client {group_name}: {e}")
+                        except Exception as e:
+                            logger.exception("Error processing MCP client %s: %s", group_name, e)
                             mcp_clients_info.append({

1116-1124: Avoid accessing private builder state (_function_groups).

This breaks encapsulation and may cause future breakage. Prefer a public accessor on WorkflowBuilder (e.g., get_function_groups()), or extend the builder API to supply the required data to routes.

If helpful, I can propose a small WorkflowBuilder accessor and update this call site.

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)

204-215: Tighten schema normalization; remove unused noqa and keep types clean.

  • The # noqa: E721 is unnecessary since you’re using is.
  • Keep the normalization but drop the comment to satisfy ruff RUF100.
-            # Normalize optional typing for linter/type-checker compatibility
+            # Normalize optional typing for linter/type-checker compatibility
             single_fn = tool_fn.single_fn
             if single_fn is None:
                 # Should not happen because mcp_tool_function always sets a single_fn
                 logger.warning("Skipping tool %s because single_fn is None", function_name)
                 continue

             input_schema = tool_fn.input_schema
-            # Convert NoneType sentinel to None for FunctionGroup.add_function signature
-            if input_schema is type(None):  # noqa: E721
+            # Convert NoneType sentinel to None for FunctionGroup.add_function signature
+            if input_schema is type(None):
                 input_schema = None
@@
-                               fn=single_fn,
-                               input_schema=input_schema,
+                               fn=single_fn,
+                               input_schema=input_schema,

Also applies to: 218-222

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74a2ca8 and 8c595ab.

📒 Files selected for processing (4)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (3 hunks)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (2 hunks)
  • src/nat/cli/commands/mcp/mcp.py (1 hunks)
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • src/nat/cli/commands/mcp/mcp.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • src/nat/cli/commands/mcp/mcp.py
packages/*/src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Importable Python code inside packages must live under packages//src/

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • src/nat/cli/commands/mcp/mcp.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • src/nat/cli/commands/mcp/mcp.py
packages/**/*

⚙️ CodeRabbit configuration file

packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain a pyproject.toml file. - The pyproject.toml file should declare a dependency on nvidia-nat or another package with a name starting
with nvidia-nat-. This dependency should be declared using ~=<version>, and the version should be a two
digit version (ex: ~=1.0).

  • Not all packages contain Python code, if they do they should also contain their own set of tests, in a
    tests/ directory at the same level as the pyproject.toml file.

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All importable Python code must live under src/ (or packages//src/)

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • src/nat/cli/commands/mcp/mcp.py
src/nat/**/*

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Changes in src/nat should prioritize backward compatibility

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • src/nat/cli/commands/mcp/mcp.py

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • src/nat/cli/commands/mcp/mcp.py
🧬 Code graph analysis (3)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (7)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • transport (171-172)
  • input_schema (534-538)
  • name (520-522)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (4)
src/nat/builder/workflow_builder.py (2)
  • get_tools (541-572)
  • get_tools (1176-1185)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (7)
  • name (520-522)
  • get_tools (267-301)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • transport (171-172)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)
  • MCPClientConfig (89-124)
src/nat/builder/function.py (1)
  • get_accessible_functions (485-521)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exception_handler.py (1)
  • mcp_exception_handler (199-211)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py (1)
  • MCPTimeoutError (60-71)
🪛 Ruff (0.13.1)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py

185-185: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


186-186: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


187-187: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


213-213: Unused noqa directive (unused: E721)

Remove unused noqa directive

(RUF100)

src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py

1130-1130: Abstract raise to an inner function

(TRY301)


1130-1130: Avoid specifying long messages outside the exception class

(TRY003)


1139-1139: Do not catch blind exception: Exception

(BLE001)


1153-1153: Do not catch blind exception: Exception

(BLE001)


1164-1165: try-except-pass detected, consider logging the exception

(S110)


1164-1164: Do not catch blind exception: Exception

(BLE001)


1199-1199: Do not catch blind exception: Exception

(BLE001)


1200-1200: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1214-1214: Do not catch blind exception: Exception

(BLE001)


1215-1215: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1216-1216: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


1216-1216: Use explicit conversion flag

Replace with conversion flag

(RUF010)

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py

282-282: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ 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: CI Pipeline / Check
🔇 Additional comments (2)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (1)

244-245: New route wiring looks correct.

The new MCP client tool list endpoint is registered alongside existing routes. No issues in ordering or invocation.

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)

176-190: LGTM: client selection and context-managed connection.

Transport selection, auth resolution, and async with client: pattern are sound. Exposing the live client on the group is pragmatic for reuse.

Please ensure FunctionGroup allows dynamic attribute assignment across all supported runtimes. If it uses __slots__, this will fail; otherwise, it’s fine.

Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Copy link

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

🧹 Nitpick comments (8)
docs/source/workflows/mcp/mcp-client.md (1)

257-349: Document response fields explicitly for the new endpoint.

Consider adding a brief bullet list of response fields (function_group, server, transport, session_healthy, tools[].{name,description,server,available}, total_tools, available_tools) right under “Endpoint” for quick scanning, keeping the sample JSON as-is.

As per coding guidelines

tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (7)

39-41: Fix Ruff TRY003 by avoiding exception message literal.

Ruff hint flags raising with a message. A bare RuntimeError is sufficient in this stub.

Apply this diff:

-        if self._raise:
-            raise RuntimeError("Failed to get tools")
+        if self._raise:
+            raise RuntimeError

As per coding guidelines


75-82: Rename and type the fixture per repo conventions.

Fixtures should use the fixture_ prefix and set name in the decorator; add return type.

Apply this diff:

-@pytest_asyncio.fixture
-async def app_worker():
+@pytest_asyncio.fixture(name="app_worker")
+async def fixture_app_worker() -> tuple[FastAPI, FastApiFrontEndPluginWorker]:
     cfg = Config()
     worker = FastApiFrontEndPluginWorker(cfg)
     app = FastAPI()
     worker.set_cors_config(app)
     return app, worker

As per coding guidelines


18-20: Prefer httpx.AsyncClient for async tests instead of TestClient.

Using TestClient inside async tests can cause nested event loop issues. Switch to httpx.AsyncClient with ASGITransport.

Apply this diff:

 from fastapi import FastAPI
-from fastapi.testclient import TestClient
+import httpx

As per coding guidelines


85-88: Add type hints to test signature.

Annotate parameters and return type.

Apply this diff:

-async def test_mcp_client_tool_list_success_with_alias(app_worker):
+async def test_mcp_client_tool_list_success_with_alias(
+    app_worker: tuple[FastAPI, FastApiFrontEndPluginWorker]
+) -> None:

As per coding guidelines


109-127: Use httpx.AsyncClient with ASGITransport in async test.

Prevents flakiness with nested event loops and aligns with FastAPI testing recommendations.

Apply this diff:

-    with TestClient(app) as client_http:
-        resp = client_http.get("/mcp/client/tool/list")
-        assert resp.status_code == 200
-        data = resp.json()
+    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test") as client_http:
+        resp = await client_http.get("/mcp/client/tool/list")
+        assert resp.status_code == 200
+        data = resp.json()
         assert "mcp_clients" in data
         assert len(data["mcp_clients"]) == 1
         group = data["mcp_clients"][0]
         assert group["function_group"] == group_name
         assert group["server"].startswith("streamable-http:")
+        assert group["transport"] == "streamable-http"
         assert group["session_healthy"] is True
         assert group["total_tools"] == 1
         assert group["available_tools"] == 1
         assert len(group["tools"]) == 1
         tool = group["tools"][0]
         assert tool["name"] == "alias_tool"
         assert tool["available"] is True
         assert tool["server"].startswith("streamable-http:")
         # Prefer workflow/override description
         assert tool["description"] == "Overridden desc"

As per coding guidelines


131-139: Add type hints to test signature.

Annotate parameters and return type.

Apply this diff:

-async def test_mcp_client_tool_list_unhealthy_marks_unavailable(app_worker):
+async def test_mcp_client_tool_list_unhealthy_marks_unavailable(
+    app_worker: tuple[FastAPI, FastApiFrontEndPluginWorker]
+) -> None:

As per coding guidelines


154-165: Use httpx.AsyncClient with ASGITransport in async test (unhealthy case).

Mirror the change applied to the healthy test.

Apply this diff:

-    with TestClient(app) as client_http:
-        resp = client_http.get("/mcp/client/tool/list")
-        assert resp.status_code == 200
-        data = resp.json()
+    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test") as client_http:
+        resp = await client_http.get("/mcp/client/tool/list")
+        assert resp.status_code == 200
+        data = resp.json()
         group = data["mcp_clients"][0]
         assert group["function_group"] == group_name
         assert group["session_healthy"] is False
         assert group["total_tools"] == 2
         assert group["available_tools"] == 0
         assert len(group["tools"]) == 2
         assert all(t["available"] is False for t in group["tools"])

As per coding guidelines

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c595ab and cfddee6.

📒 Files selected for processing (2)
  • docs/source/workflows/mcp/mcp-client.md (2 hunks)
  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
tests/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Unit tests reside under tests/ and should use markers defined in pyproject.toml (e.g., integration)

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
{tests/**/*.py,examples/*/tests/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

{tests/**/*.py,examples/*/tests/**/*.py}: Use pytest (with pytest-asyncio for async); name test files test_*.py; test functions start with test_; extract repeated code into fixtures; fixtures must set name in decorator and be named with fixture_ prefix
Mock external services with pytest_httpserver or unittest.mock; do not hit live endpoints
Mark expensive tests with @pytest.mark.slow or @pytest.mark.integration

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
  • docs/source/workflows/mcp/mcp-client.md
docs/source/**/*.md

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.md: Use the official naming throughout documentation: first use “NVIDIA NeMo Agent toolkit”, subsequent “NeMo Agent toolkit”; never use deprecated names (Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq)
Documentation sources are Markdown files under docs/source; images belong in docs/source/_static
Keep docs in sync with code; documentation pipeline must pass Sphinx and link checks; avoid TODOs/FIXMEs/placeholders; avoid offensive/outdated terms; ensure spelling correctness
Do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt; accepted terms in accept.txt are allowed

Files:

  • docs/source/workflows/mcp/mcp-client.md
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/workflows/mcp/mcp-client.md
🧬 Code graph analysis (1)
tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (3)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (2)
  • set_cors_config (153-183)
  • add_mcp_client_tool_list_route (1092-1252)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2)
  • description (525-531)
  • transport (171-172)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (3)
  • MCPClientConfig (89-124)
  • MCPServerConfig (43-86)
  • MCPToolOverrideConfig (35-40)
🪛 Ruff (0.13.1)
tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py

40-40: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (1)
docs/source/workflows/mcp/mcp-client.md (1)

185-202: Section rename is clear and helpful.

“via CLI” makes the scope explicit and aligns with the parallel HTTP docs below.

Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Copy link

@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

🧹 Nitpick comments (3)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (1)

288-290: Use error level when re-raising.

When re-raising, prefer logger.error over warning.

As per coding guidelines

-        except Exception as e:
-            logger.warning("Failed to get tools: %s", e)
-            raise
+        except Exception as e:
+            logger.error("Failed to get tools: %s", e)
+            raise
tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (1)

75-82: Align pytest fixture naming with repo guidelines.

Rename function to fixture_… and set name= to keep external reference stable.

As per coding guidelines

-@pytest_asyncio.fixture
-async def app_worker():
+@pytest_asyncio.fixture(name="app_worker")
+async def fixture_app_worker():
     cfg = Config()
     worker = FastApiFrontEndPluginWorker(cfg)
     app = FastAPI()
     worker.set_cors_config(app)
     return app, worker
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)

256-258: Remove unused noqa (RUF100) and keep the comparison clean.

The E721 noqa is unnecessary here.

-            # Convert NoneType sentinel to None for FunctionGroup.add_function signature
-            if input_schema is type(None):  # noqa: E721
-                input_schema = None
+            # Convert NoneType sentinel to None for FunctionGroup.add_function signature
+            if input_schema is type(None):
+                input_schema = None
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e79a724 and f75a009.

📒 Files selected for processing (4)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2 hunks)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (3 hunks)
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (3 hunks)
  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All importable Python code must live under src/ (or packages//src/)

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
src/nat/**/*

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Changes in src/nat should prioritize backward compatibility

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
tests/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Unit tests reside under tests/ and should use markers defined in pyproject.toml (e.g., integration)

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
{tests/**/*.py,examples/*/tests/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

{tests/**/*.py,examples/*/tests/**/*.py}: Use pytest (with pytest-asyncio for async); name test files test_*.py; test functions start with test_; extract repeated code into fixtures; fixtures must set name in decorator and be named with fixture_ prefix
Mock external services with pytest_httpserver or unittest.mock; do not hit live endpoints
Mark expensive tests with @pytest.mark.slow or @pytest.mark.integration

Files:

  • tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py
packages/*/src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Importable Python code inside packages must live under packages//src/

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
packages/**/*

⚙️ CodeRabbit configuration file

packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain a pyproject.toml file. - The pyproject.toml file should declare a dependency on nvidia-nat or another package with a name starting
with nvidia-nat-. This dependency should be declared using ~=<version>, and the version should be a two
digit version (ex: ~=1.0).

  • Not all packages contain Python code, if they do they should also contain their own set of tests, in a
    tests/ directory at the same level as the pyproject.toml file.

Files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
🧠 Learnings (3)
📚 Learning: 2025-09-23T18:39:15.023Z
Learnt from: CR
PR: NVIDIA/NeMo-Agent-Toolkit#0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-09-23T18:39:15.023Z
Learning: Applies to **/*.py : Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising

Applied to files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
📚 Learning: 2025-08-28T20:03:37.650Z
Learnt from: willkill07
PR: NVIDIA/NeMo-Agent-Toolkit#684
File: src/nat/front_ends/mcp/mcp_front_end_plugin_worker.py:0-0
Timestamp: 2025-08-28T20:03:37.650Z
Learning: Function groups in NAT automatically namespace their function names with the group name (e.g., "user_report.get", "user_report.put"), making function name collisions impossible by design. No collision detection is needed when merging functions from function groups.

Applied to files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
📚 Learning: 2025-08-28T14:28:34.779Z
Learnt from: willkill07
PR: NVIDIA/NeMo-Agent-Toolkit#684
File: src/nat/builder/component_utils.py:110-114
Timestamp: 2025-08-28T14:28:34.779Z
Learning: FunctionGroupBaseConfig and FunctionBaseConfig are separate class hierarchies that both inherit from (TypedBaseModel, BaseModelRegistryTag). FunctionGroupBaseConfig does not inherit from FunctionBaseConfig, so isinstance checks between them won't interfere with each other.

Applied to files:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py
🧬 Code graph analysis (4)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (4)
src/nat/builder/function.py (1)
  • Function (46-302)
src/nat/builder/workflow_builder.py (3)
  • WorkflowBuilder (144-1114)
  • get_tools (541-572)
  • get_tools (1176-1185)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (8)
  • name (520-522)
  • description (525-531)
  • get_tools (267-301)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • transport (171-172)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (3)
  • MCPClientConfig (133-168)
  • mcp_client (49-51)
  • mcp_client (54-56)
tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py (3)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (2)
  • config (120-121)
  • add_mcp_client_tool_list_route (1093-1257)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (9)
  • description (525-531)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • get_tools (267-301)
  • transport (171-172)
  • url (362-363)
  • url (469-470)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (5)
  • mcp_client (49-51)
  • mcp_client (54-56)
  • MCPClientConfig (133-168)
  • MCPServerConfig (87-130)
  • MCPToolOverrideConfig (79-84)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exception_handler.py (1)
  • mcp_exception_handler (199-211)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py (1)
  • MCPTimeoutError (60-71)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (2)
src/nat/builder/function.py (1)
  • FunctionGroup (352-714)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (9)
  • args (421-422)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • transport (171-172)
  • input_schema (534-538)
  • name (520-522)
  • description (525-531)
🪛 Ruff (0.13.1)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py

1134-1134: Abstract raise to an inner function

(TRY301)


1134-1134: Avoid specifying long messages outside the exception class

(TRY003)


1144-1144: Redundant exception object included in logging.exception call

(TRY401)


1161-1161: Redundant exception object included in logging.exception call

(TRY401)


1170-1171: try-except-pass detected, consider logging the exception

(S110)


1170-1170: Do not catch blind exception: Exception

(BLE001)


1176-1176: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)


1204-1204: Do not catch blind exception: Exception

(BLE001)


1205-1205: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1219-1219: Do not catch blind exception: Exception

(BLE001)


1220-1220: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1221-1221: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


1221-1221: Use explicit conversion flag

Replace with conversion flag

(RUF010)

tests/nat/front_ends/fastapi/test_mcp_client_endpoint.py

40-40: Avoid specifying long messages outside the exception class

(TRY003)

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py

282-282: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py

256-256: Unused noqa directive (unused: E721)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (8)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2)

266-267: Good: public API annotated and decorated.

Adding the return type and @mcp_exception_handler aligns with typing and exception handling standards.


281-283: Chain the original timeout exception (ruff B904).

Preserve the cause when raising MCPTimeoutError.

As per coding guidelines

-            except TimeoutError as e:
-                from nat.plugins.mcp.exceptions import MCPTimeoutError
-                raise MCPTimeoutError(self.server_name, e)
+            except TimeoutError as e:
+                from nat.plugins.mcp.exceptions import MCPTimeoutError
+                raise MCPTimeoutError(self.server_name, e) from e
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (1)

228-230: LGTM: direct assignments over setattr.

Switching to property-backed assignments resolves ruff B010 and improves clarity.

src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (5)

1140-1146: Use parameterized logger.exception; avoid f-strings and passing the exception (TRY401).

logger.exception already includes the stack; keep parameters structured.

As per coding guidelines

-                        except Exception as e:
-                            logger.exception(f"Failed to connect to MCP server {client.server_name}: {e}")
+                        except Exception as e:
+                            logger.exception("Failed to connect to MCP server %s", client.server_name)
                             session_healthy = False

1160-1162: Same: parameterized logger.exception for function retrieval.

Avoid f-strings and redundant exception object.

As per coding guidelines

-                        except Exception as e:
-                            logger.exception(f"Failed to get accessible functions for group {group_name}: {e}")
+                        except Exception as e:
+                            logger.exception("Failed to get accessible functions for group %s", group_name)

1170-1171: Don’t swallow exceptions.

Log the exception instead of pass.

As per coding guidelines

-                        except Exception:
-                            pass
+                        except Exception:
+                            logger.exception("Failed to process tool overrides for group %s", group_name)

1204-1215: Use logger.exception when handling and continuing.

Include stack trace; avoid f-strings.

As per coding guidelines

-                    except Exception as e:
-                        logger.error(f"Error processing MCP client {group_name}: {e}")
+                    except Exception as e:
+                        logger.exception("Error processing MCP client %s", group_name)
                         mcp_clients_info.append({
                             "function_group": group_name,
                             "server": "unknown",
                             "transport": config.server.transport if config.server else "unknown",
                             "session_healthy": False,
-                            "error": str(e),
+                            "error": str(e),
                             "tools": [],
                             "total_tools": 0,
                             "workflow_tools": 0
                         })

1219-1221: Preserve cause and parameterize logging when re-raising (ruff B904, TRY400).

Chain HTTPException and avoid f-strings in logs.

As per coding guidelines

-            except Exception as e:
-                logger.error(f"Error in MCP client tool list endpoint: {e}")
-                raise HTTPException(status_code=500, detail=f"Failed to retrieve MCP client information: {str(e)}")
+            except Exception as e:
+                logger.error("Error in MCP client tool list endpoint: %s", e)
+                raise HTTPException(
+                    status_code=500,
+                    detail=f"Failed to retrieve MCP client information: {e!s}",
+                ) from e

Signed-off-by: Yuchen Zhang <[email protected]>
Signed-off-by: Yuchen Zhang <[email protected]>
Copy link

@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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84c4e44 and 5bf0837.

📒 Files selected for processing (1)
  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All importable Python code must live under src/ (or packages//src/)

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
src/nat/**/*

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Changes in src/nat should prioritize backward compatibility

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
🧠 Learnings (2)
📚 Learning: 2025-09-23T18:39:15.023Z
Learnt from: CR
PR: NVIDIA/NeMo-Agent-Toolkit#0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-09-23T18:39:15.023Z
Learning: Applies to **/*.py : Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising

Applied to files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
📚 Learning: 2025-08-28T20:03:37.650Z
Learnt from: willkill07
PR: NVIDIA/NeMo-Agent-Toolkit#684
File: src/nat/front_ends/mcp/mcp_front_end_plugin_worker.py:0-0
Timestamp: 2025-08-28T20:03:37.650Z
Learning: Function groups in NAT automatically namespace their function names with the group name (e.g., "user_report.get", "user_report.put"), making function name collisions impossible by design. No collision detection is needed when merging functions from function groups.

Applied to files:

  • src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
🧬 Code graph analysis (1)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (4)
src/nat/builder/function.py (2)
  • Function (46-302)
  • get_accessible_functions (486-522)
src/nat/builder/workflow_builder.py (3)
  • WorkflowBuilder (144-1114)
  • get_tools (541-572)
  • get_tools (1176-1185)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (8)
  • name (520-522)
  • description (525-531)
  • get_tools (267-301)
  • server_name (199-203)
  • server_name (366-367)
  • server_name (417-418)
  • server_name (473-474)
  • transport (171-172)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_impl.py (3)
  • MCPClientConfig (133-168)
  • mcp_client (49-51)
  • mcp_client (54-56)
🪛 Ruff (0.13.1)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py

1134-1134: Abstract raise to an inner function

(TRY301)


1134-1134: Avoid specifying long messages outside the exception class

(TRY003)


1144-1144: Redundant exception object included in logging.exception call

(TRY401)


1161-1161: Redundant exception object included in logging.exception call

(TRY401)


1174-1175: try-except-pass detected, consider logging the exception

(S110)


1174-1174: Do not catch blind exception: Exception

(BLE001)


1180-1180: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)


1215-1215: Do not catch blind exception: Exception

(BLE001)


1216-1216: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1230-1230: Do not catch blind exception: Exception

(BLE001)


1231-1231: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1232-1232: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


1232-1232: Use explicit conversion flag

Replace with conversion flag

(RUF010)

⏰ 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: CI Pipeline / Check
🔇 Additional comments (3)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (3)

1174-1175: Log override parsing failures instead of swallowing them.

Catching Exception and doing nothing violates the logging rules and hides misconfigurations, making the endpoint harder to diagnose. Please log (at minimum) before continuing. As per coding guidelines

-                        except Exception:
-                            pass
+                        except Exception:
+                            logger.exception("Failed to process tool overrides for group %s", group_name)

1178-1213: Fix nondeterministic tool pairing between functions and short names.

zip(configured_full_to_fn.values(), configured_short_names) is nondeterministic because the short names come from a set, so descriptions, availability, and alias mapping can be assigned to the wrong tool. Iterate deterministically over the configured functions and derive the short name from each key. As per coding guidelines

-                        configured_short_names: set[str] = set()
+                        configured_short_names: list[str] = []
...
-                            configured_short_names = {name.split('.', 1)[1] for name in accessible_functions.keys()}
+                            configured_short_names = [name.split('.', 1)[1] for name in accessible_functions.keys()]
...
-                        for wf_fn, fn_short in zip(configured_full_to_fn.values(), configured_short_names):
+                        for full_name, wf_fn in configured_full_to_fn.items():
+                            fn_short = full_name.split('.', 1)[1]
                             orig_name = alias_to_original.get(fn_short, fn_short)

1230-1233: Log and re-raise the outer failure correctly.

The top-level handler should use logger.exception (no f-string) and chain the original error via from exc so FastAPI keeps the traceback. As per coding guidelines

-            except Exception as e:
-                logger.error(f"Error in MCP client tool list endpoint: {e}")
-                raise HTTPException(status_code=500, detail=f"Failed to retrieve MCP client information: {str(e)}")
+            except Exception as exc:
+                logger.exception("Error in MCP client tool list endpoint")
+                raise HTTPException(
+                    status_code=500,
+                    detail=f"Failed to retrieve MCP client information: {exc!s}",
+                ) from exc

@yczhang-nv
Copy link
Contributor Author

/merge

@rapids-bot rapids-bot bot merged commit d0804b9 into NVIDIA:develop Sep 29, 2025
17 checks passed
@yczhang-nv yczhang-nv deleted the yuchen-add-mcp-tool-list-endpoint branch September 29, 2025 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants