Skip to content

LCORE-1329: Adding new MCP E2E Tests#1294

Open
jrobertboos wants to merge 8 commits intolightspeed-core:mainfrom
jrobertboos:lcore-1329
Open

LCORE-1329: Adding new MCP E2E Tests#1294
jrobertboos wants to merge 8 commits intolightspeed-core:mainfrom
jrobertboos:lcore-1329

Conversation

@jrobertboos
Copy link
Contributor

@jrobertboos jrobertboos commented Mar 9, 2026

Description

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement
  • Benchmarks improvement

Tools used to create PR

Identify any AI code assistants used in this PR (for transparency and review context)

  • Assisted-by: (e.g., Claude, CodeRabbit, Ollama, etc., N/A if not used)
  • Generated by: (e.g., tool name and version; N/A if not used)

Related Tickets & Documents

  • Related Issue #
  • Closes #

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

Summary by CodeRabbit

  • Tests
    • Added comprehensive end-to-end test coverage for MCP authentication methods including file-based, Kubernetes, OAuth, and client authentication schemes.
    • Introduced test scenarios validating authorization responses (200/401 status codes) across multiple endpoints.
    • Enhanced test infrastructure with new configuration variants and assertion utilities for broader authentication coverage.

- Introduced new YAML configuration files for MCP authentication methods: file, Kubernetes, and OAuth.
- Updated existing MCP configuration to include new authentication methods.
- Enhanced end-to-end tests to cover scenarios for file-based, Kubernetes, and OAuth authentication.
- Added utility functions for unregistering MCP toolgroups in the testing environment.
- Implemented new step definitions for checking response body content in tests.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

Walkthrough

This PR adds comprehensive MCP authentication testing infrastructure with multiple configuration scenarios across library and server modes. It introduces test configurations for file-based, Kubernetes, OAuth, and client-based authentication schemes, along with supporting utilities and test steps.

Changes

Cohort / File(s) Summary
Docker & Secrets Configuration
docker-compose.yaml, tests/e2e/secrets/invalid-mcp-token
Added volume mount for invalid MCP token secret and created the corresponding token file for testing invalid authentication paths.
MCP Configuration Files - Library Mode
tests/e2e/configuration/library-mode/lightspeed-stack-invalid-mcp-file-auth.yaml, lightspeed-stack-mcp-client-auth.yaml, lightspeed-stack-mcp-kubernetes-auth.yaml, lightspeed-stack-mcp-oauth-auth.yaml, lightspeed-stack-mcp-file-auth.yaml, lightspeed-stack-mcp.yaml
Added 6 YAML configurations for MCP authentication scenarios in library mode, each defining service settings, user data collection, authentication module (noop), and MCP server endpoints with distinct auth headers (file-based, Kubernetes, OAuth, client); refactored mcp-file-auth to mcp-file and removed provider_id fields.
MCP Configuration Files - Server Mode
tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml, lightspeed-stack-mcp-client-auth.yaml, lightspeed-stack-mcp-kubernetes-auth.yaml, lightspeed-stack-mcp-oauth-auth.yaml, lightspeed-stack-mcp-file-auth.yaml, lightspeed-stack-mcp.yaml
Added 6 YAML configurations for MCP authentication scenarios in server mode with external llama-stack integration, mirroring library-mode structure but configured for server-mode operation.
Test Infrastructure & Utilities
tests/e2e/features/environment.py, tests/e2e/features/steps/common_http.py, tests/e2e/utils/llama_stack_tools.py
Added MCP configuration selection logic in environment setup, new test step to validate response body exclusions, and utility module for unregistering MCP toolgroups via AsyncLlamaStackClient with error handling for connection and not-found scenarios.
Feature Tests
tests/e2e/features/mcp.feature
Expanded test scenarios with multiple authentication paths (file, Kubernetes, OAuth, client) across tools, query, and streaming endpoints; added 200/401 response validation, error payload assertions, and auth-failure scenarios; introduced config-specific annotations for selective test execution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding new MCP end-to-end tests, which aligns with the comprehensive additions of MCP authentication test configurations, test scenarios, and utilities throughout the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

- Added a new invalid MCP token file for testing purposes.
- Updated the Docker Compose configuration to mount the invalid MCP token.
- Introduced a new YAML configuration for testing invalid MCP file authentication.
- Enhanced the test scenarios to include checks for invalid MCP file authentication.
- Updated feature files to reflect the new authentication configurations.
…handling

- Added new configurations for invalid, Kubernetes, client, and OAuth MCP authentication methods.
- Updated scenario handling to reference the new configuration paths for Kubernetes, client, and OAuth authentication.
- Enhanced the testing environment to support the new authentication configurations.
@jrobertboos
Copy link
Contributor Author

So currently the following e2e tests fail:

Failing scenarios:
  tests/e2e/features/mcp.feature:11  Check if tools endpoint succeeds when MCP file-based auth token is passed
  tests/e2e/features/mcp.feature:49  Check if tools endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:64  Check if query endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:82  Check if streaming_query endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:101  Check if tools endpoint succeeds when MCP kubernetes auth token is passed
  tests/e2e/features/mcp.feature:142  Check if tools endpoint reports error when MCP kubernetes invalid auth token is passed
  tests/e2e/features/mcp.feature:158  Check if query endpoint reports error when MCP kubernetes invalid auth token is passed
  tests/e2e/features/mcp.feature:177  Check if streaming_query endpoint reports error when MCP kubernetes invalid auth token is passed
  tests/e2e/features/mcp.feature:197  Check if tools endpoint succeeds by skipping when MCP client-provided auth token is omitted

These failing tests can be grouped into the following bugs:
/tools does not handle authorization with static token (path) or kubernetes

  tests/e2e/features/mcp.feature:11  Check if tools endpoint succeeds when MCP file-based auth token is passed
  tests/e2e/features/mcp.feature:49  Check if tools endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:101  Check if tools endpoint succeeds when MCP kubernetes auth token is passed
  tests/e2e/features/mcp.feature:142  Check if tools endpoint reports error when MCP kubernetes invalid auth token is passed

/tools does not handle skipping toolgroup when client token is not provided

  tests/e2e/features/mcp.feature:197  Check if tools endpoint succeeds by skipping when MCP client-provided auth token is omitted

/query and /streaming_query does not catch authorization error with static token (path) or kubernetes

  tests/e2e/features/mcp.feature:64  Check if query endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:82  Check if streaming_query endpoint reports error when MCP file-based invalid auth token is passed
  tests/e2e/features/mcp.feature:158  Check if query endpoint reports error when MCP kubernetes invalid auth token is passed
  tests/e2e/features/mcp.feature:177  Check if streaming_query endpoint reports error when MCP kubernetes invalid auth token is passed

@jrobertboos jrobertboos marked this pull request as ready for review March 12, 2026 20:26
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
tests/e2e/features/mcp.feature (1)

440-447: ⚠️ Potential issue | 🟡 Minor

Use the same OAuth fixture token as the other happy-path cases.

The server-mode query happy path right above this uses Bearer oauth-test-token, but this streaming case switches to Bearer test-token. If those fixtures diverge, this scenario stops covering the same auth path and will fail independently.

🔧 Suggested fix
     And I set the "MCP-HEADERS" header to
     """
-    {"mcp-oauth": {"Authorization": "Bearer test-token"}}
+    {"mcp-oauth": {"Authorization": "Bearer oauth-test-token"}}
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/mcp.feature` around lines 440 - 447, The streaming-query
scenario "Check if streaming_query endpoint succeeds when MCP OAuth auth token
is passed" uses a different fixture token ("Bearer test-token") than the other
happy-path cases; update the MCP-HEADERS block in that Scenario to use the same
fixture token "Bearer oauth-test-token" so it matches the server-mode happy-path
tests and continues to exercise the same OAuth fixture.
🧹 Nitpick comments (3)
tests/e2e/utils/llama_stack_tools.py (2)

35-48: Explicit return None is unnecessary.

Line 45 returns None explicitly, but the function return type is None. A bare return or simply omitting it would be clearer.

✨ Minor cleanup
         if e.status_code == 400 and "not found" in str(e).lower():
-            return None
+            return
         raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/utils/llama_stack_tools.py` around lines 35 - 48, The function
_unregister_toolgroup_async contains an explicit "return None" inside the
APIStatusError handler which is redundant given the annotated return type is
None; remove the explicit "return None" (either replace with a bare "return" or
just omit the return entirely) in the block that checks if e.status_code == 400
and "not found" in str(e).lower(), leaving the rest of the error handling
(raising other errors) and the finally block that calls await client.close()
unchanged.

51-65: Multiple client instances created during iteration.

The _unregister_mcp_toolgroups_async function creates a client at line 53 to list toolgroups, then _unregister_toolgroup_async creates a new client for each toolgroup to unregister. This results in N+1 client instances for N toolgroups, each requiring cleanup.

Consider reusing the client instance:

♻️ Proposed refactor to reuse client
 async def _unregister_mcp_toolgroups_async() -> None:
     """Unregister all MCP toolgroups."""
     client = _get_llama_stack_client()
     try:
         toolgroups = await client.toolgroups.list()
         for toolgroup in toolgroups:
             if (
                 toolgroup.identifier
                 and toolgroup.provider_id == "model-context-protocol"
             ):
-                await _unregister_toolgroup_async(toolgroup.identifier)
+                try:
+                    await client.toolgroups.unregister(toolgroup.identifier)
+                except APIStatusError as e:
+                    # 400 "not found": toolgroup already absent, continue
+                    if not (e.status_code == 400 and "not found" in str(e).lower()):
+                        raise
     except APIConnectionError:
         raise
     finally:
         await client.close()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/utils/llama_stack_tools.py` around lines 51 - 65, The function
_unregister_mcp_toolgroups_async currently creates a client to list toolgroups
but calls _unregister_toolgroup_async for each item which creates its own client
per toolgroup; refactor to reuse a single Llama Stack client by modifying
_unregister_toolgroup_async (or adding a helper like
_unregister_toolgroup_with_client) to accept an existing client instance and use
that to perform the unregister operation, then call that variant from
_unregister_mcp_toolgroups_async to avoid N+1 clients; ensure the shared client
is closed once in the outer function’s finally block and preserve the existing
APIConnectionError rethrow behavior.
tests/e2e/features/mcp.feature (1)

421-428: Drop the unused mcp-client header from this server-mode-only scenario.

This scenario is already gated with @skip-in-library-mode, so it runs against the server-mode OAuth config. tests/e2e/configuration/server-mode/lightspeed-stack-mcp-oauth-auth.yaml only registers mcp-oauth on Lines 21-25, so the extra mcp-client entry does not participate in the test and makes failures harder to attribute.

✂️ Suggested cleanup
     And I set the "MCP-HEADERS" header to
     """
-    {"mcp-oauth": {"Authorization": "Bearer oauth-test-token"}, "mcp-client": {"Authorization": "Bearer client-test-token"}}
+    {"mcp-oauth": {"Authorization": "Bearer oauth-test-token"}}
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/mcp.feature` around lines 421 - 428, Remove the unused
"mcp-client" header from the MCP-HEADERS payload in the Scenario "Check if query
endpoint succeeds when MCP OAuth auth token is passed" so the test only sets
{"mcp-oauth": {"Authorization": "Bearer oauth-test-token"}}; locate the Scenario
block that sets the "MCP-HEADERS" header and delete the "mcp-client" JSON entry
to match the server-mode MCP OAuth config (registered as mcp-oauth).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/e2e/features/mcp.feature`:
- Around line 10-12: Replace blanket `@skip` tags that disable tests
unconditionally with the targeted `@skip-in-library-mode` tag so the /tools and
invalid-auth scenarios still run in non-library modes; specifically, for the
Scenario "Check if tools endpoint succeeds when MCP file-based auth token is
passed" and the other scenarios at the same locations (lines referenced in the
review), change the tag from `@skip` to `@skip-in-library-mode` (keep
`@MCPFileAuthConfig` intact) so only library-mode runs are skipped while
preserving these regression checks.

---

Outside diff comments:
In `@tests/e2e/features/mcp.feature`:
- Around line 440-447: The streaming-query scenario "Check if streaming_query
endpoint succeeds when MCP OAuth auth token is passed" uses a different fixture
token ("Bearer test-token") than the other happy-path cases; update the
MCP-HEADERS block in that Scenario to use the same fixture token "Bearer
oauth-test-token" so it matches the server-mode happy-path tests and continues
to exercise the same OAuth fixture.

---

Nitpick comments:
In `@tests/e2e/features/mcp.feature`:
- Around line 421-428: Remove the unused "mcp-client" header from the
MCP-HEADERS payload in the Scenario "Check if query endpoint succeeds when MCP
OAuth auth token is passed" so the test only sets {"mcp-oauth":
{"Authorization": "Bearer oauth-test-token"}}; locate the Scenario block that
sets the "MCP-HEADERS" header and delete the "mcp-client" JSON entry to match
the server-mode MCP OAuth config (registered as mcp-oauth).

In `@tests/e2e/utils/llama_stack_tools.py`:
- Around line 35-48: The function _unregister_toolgroup_async contains an
explicit "return None" inside the APIStatusError handler which is redundant
given the annotated return type is None; remove the explicit "return None"
(either replace with a bare "return" or just omit the return entirely) in the
block that checks if e.status_code == 400 and "not found" in str(e).lower(),
leaving the rest of the error handling (raising other errors) and the finally
block that calls await client.close() unchanged.
- Around line 51-65: The function _unregister_mcp_toolgroups_async currently
creates a client to list toolgroups but calls _unregister_toolgroup_async for
each item which creates its own client per toolgroup; refactor to reuse a single
Llama Stack client by modifying _unregister_toolgroup_async (or adding a helper
like _unregister_toolgroup_with_client) to accept an existing client instance
and use that to perform the unregister operation, then call that variant from
_unregister_mcp_toolgroups_async to avoid N+1 clients; ensure the shared client
is closed once in the outer function’s finally block and preserve the existing
APIConnectionError rethrow behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b5f69e52-f673-4161-9333-abb5b7d2fdbd

📥 Commits

Reviewing files that changed from the base of the PR and between e73778a and 62f8a53.

📒 Files selected for processing (18)
  • docker-compose.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-invalid-mcp-file-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-client-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-file-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-kubernetes-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-oauth-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-client-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-file-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-kubernetes-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-oauth-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp.yaml
  • tests/e2e/features/environment.py
  • tests/e2e/features/mcp.feature
  • tests/e2e/features/steps/common_http.py
  • tests/e2e/secrets/invalid-mcp-token
  • tests/e2e/utils/llama_stack_tools.py

Comment on lines +10 to +12
@skip
@MCPFileAuthConfig
Scenario: Check if tools endpoint succeeds when MCP file-based auth token is passed
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't merge these scenarios behind blanket @skip tags.

The targeted @skip-in-library-mode cases are understandable, but these plain @skips disable the exact /tools and invalid-auth paths called out as broken in the PR notes. That leaves the new MCP suite green without actually protecting those regressions in any mode.

Also applies to: 49-51, 65-67, 84-86, 104-106, 146-148, 163-165, 183-185, 204-206

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/mcp.feature` around lines 10 - 12, Replace blanket `@skip`
tags that disable tests unconditionally with the targeted `@skip-in-library-mode`
tag so the /tools and invalid-auth scenarios still run in non-library modes;
specifically, for the Scenario "Check if tools endpoint succeeds when MCP
file-based auth token is passed" and the other scenarios at the same locations
(lines referenced in the review), change the tag from `@skip` to
`@skip-in-library-mode` (keep `@MCPFileAuthConfig` intact) so only library-mode runs
are skipped while preserving these regression checks.

)


def _get_llama_stack_client() -> AsyncLlamaStackClient:
Copy link
Contributor

Choose a reason for hiding this comment

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

there is already file that contains this logic, merge it together with the llama_stack_shields.py

Then The status code of the response is 200
And The response should contain following fragments
| Fragments in LLM response |
| Hello |
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add And The body of the response does not contain mcp-client

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants