Skip to content

Mandate user id for MCP oauth2 authentication#873

Merged
rapids-bot[bot] merged 17 commits intoNVIDIA:developfrom
AnuradhaKaruppiah:ak-user-isolation
Sep 30, 2025
Merged

Mandate user id for MCP oauth2 authentication#873
rapids-bot[bot] merged 17 commits intoNVIDIA:developfrom
AnuradhaKaruppiah:ak-user-isolation

Conversation

@AnuradhaKaruppiah
Copy link
Contributor

@AnuradhaKaruppiah AnuradhaKaruppiah commented Sep 29, 2025

Description

NAT Oauth2 authenticate abstraction allows headless tool use without user-id. For MCP authentication user-id is crucial for cache maintanence and is mandated for tool calls.

Sample config:

authentication:
  mcp_oauth2_jira:
    _type: mcp_oauth2
    server_url: ${CORPORATE_MCP_JIRA_URL}
    redirect_uri: http://localhost:8000/auth/redirect
    default_user_id: ${CORPORATE_MCP_JIRA_URL}
    allow_default_user_id_for_tool_calls: false >>>>>>>>>>>>>>

If default_user_id is disallowed for tool calls curl to the server endpoint will fail.

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

  • Bug Fixes

    • Enforces a non-empty user identity for OAuth2 authentication and blocks tool calls without a valid session, failing fast with a clear authorization error.
  • Tests

    • Added tests to verify authentication requires a user identity and that unauthorized calls are rejected.
  • Chores

    • Added configuration defaults for MCP OAuth2 auth behavior (default user ID and fallback toggle).

Binfeng Xu and others added 12 commits September 26, 2025 23:36
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
nit
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
nit
Signed-off-by: Binfeng Xu <binengx@nvidia.com>
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
@copy-pr-bot
Copy link

copy-pr-bot bot commented Sep 29, 2025

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai
Copy link

coderabbitai bot commented Sep 29, 2025

Walkthrough

Requires explicit user/session identification for MCP tool and OAuth flows: auth provider now rejects empty user_id; MCP tool client centralizes session extraction and refuses tool calls without a session when auth is enabled; tests and a config default were added.

Changes

Cohort / File(s) Summary of Changes
MCP OAuth2 auth guard
packages/nvidia_nat_mcp/src/nat/plugins/mcp/auth/auth_provider.py
Added early check requiring non-empty user_id in OAuth2 authenticate paths; raises RuntimeError("User is not authorized to call the tool") when absent. No exported signature changes.
MCP client session/auth flow
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
Enforced use of session_id for tool calls when present; added MCPToolClient._get_session_id; acall now uses _get_session_id and raises when auth is enabled and no session_id; propagated auth callback; added MCPBaseClient.auth_provider property.
Tests: auth provider and client session
tests/nat/mcp/test_mcp_auth_provider.py, tests/nat/mcp/test_mcp_client_base.py
Added test verifying MCPOAuth2Provider.authenticate raises when user_id is missing; added tests covering _get_session_id behavior and fallback rules under various cookie/config scenarios.
Example config defaults
examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
Added default_user_id and allow_default_user_id_for_tool_calls keys under mcp_oauth2_jira configuration (defaults only).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Tool as MCPToolClient
  participant Ctx as Context (cookies/auth cb)
  participant Base as MCPBaseClient
  participant Auth as MCPOAuth2Provider

  Note over Tool,Auth: Tool call with authentication enabled

  Tool->>Tool: _get_session_id(ctx)
  Tool->>Ctx: read cookies / auth callback
  Ctx-->>Tool: session_id or None
  Tool->>Base: access auth_provider
  Base-->>Tool: Auth provider instance

  alt session_id present
    Tool->>Auth: authenticate(user_id=session_id)
    Auth-->>Tool: tokens/headers
    Tool->>Tool: proceed with tool call using headers
  else no session_id
    Tool-->>Tool: raise RuntimeError("User is not authorized to call the tool")
  end
Loading
sequenceDiagram
  autonumber
  participant Caller as Any Caller
  participant Auth as MCPOAuth2Provider

  Note over Caller,Auth: Authentication guard

  Caller->>Auth: authenticate(user_id)
  alt user_id is empty/None
    Auth-->>Caller: RuntimeError("User is not authorized to call the tool")
  else valid user_id
    Auth-->>Caller: proceed with OAuth2 flow
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

breaking

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 pull request title clearly describes the main change by stating the requirement to mandate a user identifier in the MCP OAuth2 authentication flow, uses imperative mood, and stays within the recommended length, satisfying the title guidelines.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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

@AnuradhaKaruppiah AnuradhaKaruppiah changed the title Mandate user id for MCP oauth2 authetication Mandate user id for MCP oauth2 authentication Sep 29, 2025
@AnuradhaKaruppiah
Copy link
Contributor Author

/ok to test 535c221

@AnuradhaKaruppiah AnuradhaKaruppiah added non-breaking Non-breaking change improvement Improvement to existing functionality labels Sep 29, 2025
@AnuradhaKaruppiah AnuradhaKaruppiah marked this pull request as ready for review September 29, 2025 23:47
@AnuradhaKaruppiah AnuradhaKaruppiah requested a review from a team as a code owner September 29, 2025 23:47
@coderabbitai coderabbitai bot added breaking Breaking change external This issue was filed by someone outside of the NeMo Agent toolkit team labels Sep 29, 2025
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 (2)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (2)

123-124: Clarify the session-based tool call behavior in the comment.

The updated comment states "Tool call requests should use the session id" but doesn't mention what happens if session_id is None. Consider making this more explicit.

Apply this diff to improve the comment clarity:

-                # Tool call requests should use the session id
+                # Tool call requests must use the session id; if None, auth will fail downstream
                 user_id = session_id

634-638: Consider centralizing the authorization error message.

The error message "User is not authorized to call the tool" appears in multiple locations:

  • auth_provider.py line 326
  • client_base.py line 636
  • Tests validating this message

While the duplication is minimal, consider defining a constant in a shared location (e.g., exceptions.py) to ensure consistency if the message needs to change in the future.

Example approach:

In packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py:

# Authorization error messages
UNAUTHORIZED_TOOL_CALL_MSG = "User is not authorized to call the tool"

Then import and use this constant in both files.

📜 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 c3107c5 and 535c221.

📒 Files selected for processing (3)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/auth/auth_provider.py (1 hunks)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (4 hunks)
  • tests/nat/mcp/test_mcp_auth_provider.py (1 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/auth/auth_provider.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • tests/nat/mcp/test_mcp_auth_provider.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/auth/auth_provider.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • tests/nat/mcp/test_mcp_auth_provider.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/auth/auth_provider.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/auth/auth_provider.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.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/auth/auth_provider.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py
  • tests/nat/mcp/test_mcp_auth_provider.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/auth/auth_provider.py
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.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/mcp/test_mcp_auth_provider.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/mcp/test_mcp_auth_provider.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/mcp/test_mcp_auth_provider.py
🧬 Code graph analysis (2)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (4)
src/nat/authentication/interfaces.py (2)
  • AuthProviderBase (29-72)
  • config (45-54)
src/nat/builder/context.py (2)
  • Context (115-299)
  • user_auth_callback (269-286)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py (1)
  • MCPError (30-43)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exception_handler.py (1)
  • convert_to_mcp_error (114-144)
tests/nat/mcp/test_mcp_auth_provider.py (2)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/auth/auth_provider.py (2)
  • MCPOAuth2Provider (283-398)
  • authenticate (313-332)
src/nat/authentication/oauth2/oauth2_auth_code_flow_provider.py (1)
  • authenticate (80-127)
🪛 Ruff (0.13.1)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/auth/auth_provider.py

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

(TRY003)

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

630-630: Do not catch blind exception: Exception

(BLE001)

⏰ 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 (5)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/auth/auth_provider.py (1)

324-327: LGTM! User ID validation correctly enforces authorization requirement.

The early guard prevents tool calls without an authorized user, aligning with the PR objective to mandate user_id for MCP OAuth2 authentication.

Note: The static analysis hint (TRY003) suggests moving the error message to a custom exception class. However, since this message must match the error handling in client_base.py (line 636) and tests, keeping it inline is acceptable for now. Consider creating a dedicated exception class (e.g., MCPAuthorizationError) in a future refactor to centralize this error message.

tests/nat/mcp/test_mcp_auth_provider.py (1)

297-306: LGTM! Test correctly validates user_id requirement.

The test comprehensively covers both scenarios where user_id is missing:

  1. No user_id argument provided
  2. Explicit user_id=None

Both cases correctly assert the expected RuntimeError message, ensuring the authentication guard works as intended.

packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (3)

184-186: LGTM! Clean property exposure for auth_provider.

The property correctly exposes the internal _auth_provider attribute, enabling downstream code to access the authentication provider when needed. The type hint properly indicates the optional nature of the provider.


592-616: Well-structured session extraction helper.

The _get_session_id method centralizes session ID extraction logic and handles multiple concerns:

  • Lazily sets the auth callback from context
  • Extracts session_id from cookies
  • Falls back to default_user_id when allowed

The implementation is clean and follows the single responsibility principle.


628-638: Authorization check correctly enforces user requirement for authenticated tool calls.

The logic properly raises an authorization error when authentication is enabled but no session_id is available, preventing unauthorized tool access. The error message matches the one used in auth_provider.py line 326, maintaining consistency.

Regarding the static analysis hint (BLE001) on line 630: The bare Exception catch is acceptable here because:

  1. The exception is immediately discarded and session_id is set to None
  2. The subsequent authorization check (lines 635-638) handles the missing session_id case
  3. Any context access failure shouldn't block the authorization decision

@AnuradhaKaruppiah AnuradhaKaruppiah removed breaking Breaking change external This issue was filed by someone outside of the NeMo Agent toolkit team labels Sep 29, 2025
Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
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: 1

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

592-616: Good consolidation of session extraction logic.

The helper method centralizes session_id extraction from context and correctly handles the auth callback propagation. The fallback to default_user_id is properly gated by the allow_default_user_id_for_tool_calls configuration flag.

Consider enhancing the docstring to document the return value semantics more explicitly:

     def _get_session_id(self) -> str | None:
         """
         Get the session id from the context.
+
+        Returns:
+            Session ID from context cookies, or default_user_id if allowed by config, or 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 535c221 and 6aa4b6d.

📒 Files selected for processing (1)
  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (4 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:

  • packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.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_base.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
{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_base.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_base.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
🧬 Code graph analysis (1)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (4)
src/nat/authentication/interfaces.py (2)
  • AuthProviderBase (29-72)
  • config (45-54)
src/nat/builder/context.py (2)
  • Context (115-299)
  • user_auth_callback (269-286)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exceptions.py (1)
  • MCPError (30-43)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/exception_handler.py (1)
  • convert_to_mcp_error (114-144)
🪛 Ruff (0.13.1)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py

631-631: Do not catch blind exception: Exception

(BLE001)

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

184-186: LGTM: Clean property exposure for auth provider.

The read-only property correctly exposes the authentication provider to child components (MCPToolClient) for authorization checks.


635-639: Excellent enforcement of mandatory user authorization.

The authorization check correctly implements the PR objective: when authentication is enabled, tool calls now require a valid session_id. The error message is clear, and the conversion to MCPError ensures consistent error handling across the MCP client.


122-124: Clear distinction between tool call and non-tool call authentication.

The comment accurately describes the behavior: tool call requests use only the session_id (no fallback), while non-tool calls can fall back to default_user_id. This distinction supports the PR objective of mandating user_id for tool calls.

Copy link
Contributor

@yczhang-nv yczhang-nv left a comment

Choose a reason for hiding this comment

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

LGTM

@AnuradhaKaruppiah
Copy link
Contributor Author

/ok to test 6aa4b6d

Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
@coderabbitai coderabbitai bot added the breaking Breaking change label Sep 30, 2025
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: 1

🧹 Nitpick comments (2)
tests/nat/mcp/test_mcp_client_base.py (2)

554-728: Consider extracting repeated mock setup into pytest fixtures.

The test class contains significant code duplication across test methods. The setup of mock_session, mock_parent_client, mock_metadata, and MCPToolClient instance is repeated in each test method.

Per coding guidelines, frequently repeated code should be extracted into pytest fixtures. Consider creating fixtures for common setup:

@pytest.fixture(name="mock_tool_client_no_auth")
def fixture_mock_tool_client_no_auth():
    """Fixture providing MCPToolClient with no auth provider."""
    mock_session = MagicMock()
    mock_parent_client = MagicMock()
    mock_parent_client.auth_provider = None
    
    return MCPToolClient(
        session=mock_session,
        parent_client=mock_parent_client,
        tool_name="test_tool",
        tool_description="Test tool"
    )

@pytest.fixture(name="mock_tool_client_with_auth")
def fixture_mock_tool_client_with_auth():
    """Fixture providing MCPToolClient with auth provider configured."""
    mock_session = MagicMock()
    mock_parent_client = MagicMock()
    mock_auth_provider = MagicMock()
    mock_auth_config = MagicMock()
    mock_auth_provider.config = mock_auth_config
    mock_parent_client.auth_provider = mock_auth_provider
    
    return MCPToolClient(
        session=mock_session,
        parent_client=mock_parent_client,
        tool_name="test_tool",
        tool_description="Test tool"
    ), mock_auth_config

@pytest.fixture(name="mock_context_with_cookies")
def fixture_mock_context_with_cookies():
    """Fixture providing mock context metadata with cookies."""
    mock_metadata = MagicMock()
    mock_metadata.cookies = {"nat-session": "test-session-123"}
    return mock_metadata

Then simplify tests like:

def test_get_session_id_from_cookies(self, mock_tool_client_no_auth, mock_context_with_cookies):
    """Test that session_id is correctly extracted from cookies."""
    from nat.builder.context import Context as _Ctx
    
    with patch.object(_Ctx, 'get') as mock_ctx_get:
        mock_ctx_get.return_value.metadata = mock_context_with_cookies
        session_id = mock_tool_client_no_auth._get_session_id()
        assert session_id == "test-session-123"

As per coding guidelines.


554-728: Consider adding test coverage for user_auth_callback interaction.

Based on the relevant code snippets (client_base.py lines 534-564), _get_session_id() retrieves and sets user_auth_callback on the parent client when present:

auth_callback = _Ctx.get().user_auth_callback
if auth_callback and self._parent_client:
    self._parent_client.set_user_auth_callback(auth_callback)

Consider adding a test case to verify this side effect:

def test_get_session_id_sets_auth_callback_when_present(self):
    """Test that user_auth_callback is set on parent_client when present."""
    from nat.builder.context import Context as _Ctx
    from nat.plugins.mcp.client_base import MCPToolClient
    
    mock_session = MagicMock()
    mock_parent_client = MagicMock()
    mock_parent_client.auth_provider = None
    mock_parent_client.set_user_auth_callback = MagicMock()
    
    tool_client = MCPToolClient(
        session=mock_session,
        parent_client=mock_parent_client,
        tool_name="test_tool",
        tool_description="Test tool"
    )
    
    mock_metadata = MagicMock()
    mock_metadata.cookies = {"nat-session": "test-session-123"}
    mock_auth_callback = MagicMock()
    
    with patch.object(_Ctx, 'get') as mock_ctx_get:
        mock_ctx_instance = mock_ctx_get.return_value
        mock_ctx_instance.metadata = mock_metadata
        mock_ctx_instance.user_auth_callback = mock_auth_callback
        
        session_id = tool_client._get_session_id()
        
        # Verify callback was set
        mock_parent_client.set_user_auth_callback.assert_called_once_with(mock_auth_callback)
        assert session_id == "test-session-123"
📜 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 6aa4b6d and cf09ea5.

📒 Files selected for processing (2)
  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml (1 hunks)
  • tests/nat/mcp/test_mcp_client_base.py (1 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:

  • tests/nat/mcp/test_mcp_client_base.py
  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
**/*.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/mcp/test_mcp_client_base.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/mcp/test_mcp_client_base.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/mcp/test_mcp_client_base.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/mcp/test_mcp_client_base.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/mcp/test_mcp_client_base.py
  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
**/*.{yaml,yml}

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

In workflow/config YAML, set llms.._type: nat_test_llm to stub responses.

Files:

  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
**/configs/**

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

Configuration files consumed by code must be stored next to that code in a configs/ folder

Files:

  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
examples/**/*

⚙️ CodeRabbit configuration file

examples/**/*: - This directory contains example code and usage scenarios for the toolkit, at a minimum an example should
contain a README.md or file README.ipynb.

  • If an example contains Python code, it should be placed in a subdirectory named src/ and should
    contain a pyproject.toml file. Optionally, it might also contain scripts in a scripts/ directory.
  • If an example contains YAML files, they should be placed in a subdirectory named configs/. - If an example contains sample data files, they should be placed in a subdirectory named data/, and should
    be checked into git-lfs.

Files:

  • examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml
🧬 Code graph analysis (1)
tests/nat/mcp/test_mcp_client_base.py (2)
src/nat/builder/context.py (1)
  • Context (115-299)
packages/nvidia_nat_mcp/src/nat/plugins/mcp/client_base.py (3)
  • MCPToolClient (535-665)
  • auth_provider (185-186)
  • _get_session_id (592-616)
🔇 Additional comments (2)
examples/MCP/simple_auth_mcp/configs/config-mcp-auth-jira.yml (1)

46-46: LGTM! Backward-compatible default preserves existing behavior.

The allow_default_user_id_for_tool_calls configuration with a default of true appropriately maintains backward compatibility while enabling the new user isolation feature when explicitly disabled.

tests/nat/mcp/test_mcp_client_base.py (1)

557-727: LGTM! Comprehensive test coverage for session_id extraction logic.

The test class thoroughly validates _get_session_id behavior across various scenarios:

  • Cookie-based session extraction
  • Fallback to default_user_id with proper authorization checks
  • Edge cases with missing cookies, missing nat-session, and no auth provider

The tests follow pytest conventions with clear naming, proper mocking, and the arrange-act-assert pattern.

@AnuradhaKaruppiah
Copy link
Contributor Author

/ok to test cf09ea5

@AnuradhaKaruppiah AnuradhaKaruppiah removed the breaking Breaking change label Sep 30, 2025
@AnuradhaKaruppiah
Copy link
Contributor Author

/merge

@rapids-bot rapids-bot bot merged commit fb9a973 into NVIDIA:develop Sep 30, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improvement to existing functionality non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants