Skip to content

LCORE-1329: Adding new MCP E2E Tests#1327

Merged
tisnik merged 1 commit intolightspeed-core:mainfrom
jrobertboos:lcore-1329
Mar 18, 2026
Merged

LCORE-1329: Adding new MCP E2E Tests#1327
tisnik merged 1 commit intolightspeed-core:mainfrom
jrobertboos:lcore-1329

Conversation

@jrobertboos
Copy link
Contributor

@jrobertboos jrobertboos commented Mar 16, 2026

Description

E2E tests were added to cover all of the types of MCP authorization (static token, kubernetes, client, oauth). For each type of MCP Auth all endpoints that could be affected by auth are tested on each path (valid, invalid, omitted).

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 LCORE-1329
  • Closes LCORE-1329

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

    • Expanded MCP end-to-end coverage for OAuth, Kubernetes, file, and client auth with success/failure, invalid-token and missing-credentials scenarios; removed an obsolete file-based feature.
    • Added multiple new test configurations and an invalid-token fixture; improved scenario setup/teardown to select configs, restart services, and clear storage; added a negative response assertion and placeholder step.
  • Documentation

    • Updated e2e testing docs to reflect new utilities and MCP toolgroup/shield workflows.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds comprehensive MCP e2e auth permutations (file, invalid-file, kubernetes, client, oauth), new test configs and secret, expands MCP feature tests, updates environment hooks to pick/restore MCP configs and perform mode-specific cleanup, and adds utilities to unregister MCP toolgroups and clear Llama Stack storage.

Changes

Cohort / File(s) Summary
New per-auth configs (library & server)
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-kubernetes-auth.yaml, tests/e2e/configuration/library-mode/lightspeed-stack-mcp-oauth-auth.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-kubernetes-auth.yaml, tests/e2e/configuration/server-mode/lightspeed-stack-mcp-oauth-auth.yaml
Added configurations exercising each MCP auth variant for both library and server modes.
Aggregate MCP configs
tests/e2e/configuration/library-mode/lightspeed-stack-mcp.yaml, tests/e2e/configuration/server-mode/lightspeed-stack-mcp.yaml
Removed provider_id from an entry and added mcp-kubernetes, mcp-file, and mcp-client entries with url and Authorization values (including file-path token).
Feature tests (added/removed)
tests/e2e/features/mcp.feature, tests/e2e/features/mcp_file_auth.feature
Replaced standalone mcp_file_auth.feature (deleted) with an expanded mcp.feature covering file, invalid-file, kubernetes, client, and oauth auth flows including success and 401/error cases and streaming variants.
Environment hooks
tests/e2e/features/environment.py
Extended before/after scenario hooks to detect MCP-related tags, select/restore configs, perform mode-specific cleanup (unregister MCP toolgroups in server mode or clear storage in library mode), update context.scenario_config, and restart lightspeed-stack.
Llama Stack utilities
tests/e2e/utils/llama_stack_utils.py
Renamed/expanded utilities: added async helpers to unregister MCP toolgroups (_unregister_toolgroup_async, _unregister_mcp_toolgroups_async) and public wrapper unregister_mcp_toolgroups(); added shield-unregister helper stub and updated module docstring.
Storage & restart utils
tests/e2e/utils/utils.py
Added clear_llama_stack_storage() to remove ~/.llama inside container (skips in Prow); related restart/health-check behavior updated in utils.
Step definitions
tests/e2e/features/steps/common_http.py, tests/e2e/features/steps/place_holder.py
Added check_response_body_does_not_contain() step and a placeholder step The "{mcp_server}" mcp server "{header_name}" header is set to "{header_value}" (no-op implementation).
Secrets & docker-compose
tests/e2e/secrets/invalid-mcp-token, docker-compose.yaml
Added tests/e2e/secrets/invalid-mcp-token containing invalid-token and mounted it into lightspeed-stack via a new volume mapping.
Docs & test list / rename
docs/e2e_testing.md, tests/e2e/test_list.txt, tests/e2e/utils/llama_stack_utils.py
Docs updated to reference llama_stack_utils.py (rename from shields); removed features/mcp_file_auth.feature from tests/e2e/test_list.txt.

Sequence Diagram(s)

sequenceDiagram
    participant TestRunner
    participant EnvHook as Environment Hook
    participant Docker as Docker/Compose
    participant LCS as Lightspeed Stack
    participant MCP as Mock MCP

    TestRunner->>EnvHook: start scenario with MCP tag
    EnvHook->>EnvHook: resolve config & selected cleanup
    EnvHook->>EnvHook: if server -> unregister_mcp_toolgroups()\nif library -> clear_llama_stack_storage()
    EnvHook->>Docker: apply config files & mount secrets
    EnvHook->>LCS: restart service
    TestRunner->>LCS: send API request (tools/query/stream)
    LCS->>MCP: forward request with configured Authorization
    MCP-->>LCS: respond (200 / 401)
    LCS-->>TestRunner: return response
    TestRunner->>EnvHook: teardown -> restore config & cleanup
Loading

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 and specifically describes the main change: adding new MCP (Model Context Protocol) end-to-end tests covering multiple authorization types.
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
📝 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/steps/common_http.py`:
- Around line 181-197: There are two identical step definitions for the
`@then`("The body of the response does not contain {substring}") decorator
(function check_response_body_does_not_contain), causing AmbiguousStep and F811
errors; remove the duplicate definition so only the first
check_response_body_does_not_contain implementation remains, leaving its
assertion and docstring intact and deleting the second repeated block.

In `@tests/e2e/utils/llama_stack_utils.py`:
- Around line 40-53: The docstring for _unregister_toolgroup_async is
misleading—it says it returns (provider_id, provider_shield_id) but the function
signature and implementation return None; update the docstring in
_unregister_toolgroup_async to accurately state that it unregisters the
toolgroup and returns None (or remove the restoration tuple reference), making
sure to reference the function name _unregister_toolgroup_async and not change
the current return behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2c485079-4707-4284-b15d-82140aa7f41f

📥 Commits

Reviewing files that changed from the base of the PR and between 84faf9f and 128906f.

📒 Files selected for processing (22)
  • docker-compose.yaml
  • docs/e2e_testing.md
  • 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/mcp_file_auth.feature
  • tests/e2e/features/steps/common_http.py
  • tests/e2e/secrets/invalid-mcp-token
  • tests/e2e/test_list.txt
  • tests/e2e/utils/llama_stack_utils.py
  • tests/e2e/utils/utils.py
💤 Files with no reviewable changes (2)
  • tests/e2e/test_list.txt
  • tests/e2e/features/mcp_file_auth.feature

Comment on lines +40 to +53
async def _unregister_toolgroup_async(identifier: str) -> None:
"""Unregister a toolgroup by identifier; return (provider_id, provider_shield_id) for restore."""
client = _get_llama_stack_client()
try:
await client.toolgroups.unregister(identifier)
except APIConnectionError:
raise
except APIStatusError as e:
# 400 "not found": toolgroup already absent, scenario can proceed
if e.status_code == 400 and "not found" in str(e).lower():
return None
raise
finally:
await client.close()
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 16, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading docstring: function returns None, not a tuple.

The docstring says "return (provider_id, provider_shield_id) for restore" but the function signature is -> None and the implementation only returns None. This appears to be copied from _unregister_shield_async which does return restoration data.

📝 Proposed fix for the docstring
 async def _unregister_toolgroup_async(identifier: str) -> None:
-    """Unregister a toolgroup by identifier; return (provider_id, provider_shield_id) for restore."""
+    """Unregister a toolgroup by identifier."""
     client = _get_llama_stack_client()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/utils/llama_stack_utils.py` around lines 40 - 53, The docstring for
_unregister_toolgroup_async is misleading—it says it returns (provider_id,
provider_shield_id) but the function signature and implementation return None;
update the docstring in _unregister_toolgroup_async to accurately state that it
unregisters the toolgroup and returns None (or remove the restoration tuple
reference), making sure to reference the function name
_unregister_toolgroup_async and not change the current return behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

@coderabbitai create issue

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo lightspeed-core/lightspeed-stack \
  --title "Fix misleading docstring in _unregister_toolgroup_async" \
  --body "## Summary

The docstring for \`_unregister_toolgroup_async\` in \`tests/e2e/utils/llama_stack_utils.py\` is misleading. It says:

> \"Unregister a toolgroup by identifier; return (provider_id, provider_shield_id) for restore.\"

However, the function signature is \`-> None\` and the implementation only ever returns \`None\`. This docstring appears to have been copied from \`_unregister_shield_async\`, which does return restoration data.

## Suggested Fix

\`\`\`diff
 async def _unregister_toolgroup_async(identifier: str) -> None:
-    \"\"\"Unregister a toolgroup by identifier; return (provider_id, provider_shield_id) for restore.\"\"\"
+    \"\"\"Unregister a toolgroup by identifier.\"\"\"
     client = _get_llama_stack_client()
\`\`\`

## References

- PR: https://github.com/lightspeed-core/lightspeed-stack/pull/1327
- Review comment: https://github.com/lightspeed-core/lightspeed-stack/pull/1327#discussion_r2941205171

Requested by `@tisnik`." \
  --assignee tisnik

Length of output: 63


@tisnik, I've created the follow-up issue for the misleading docstring in _unregister_toolgroup_async: #1346

Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

LGTM in overall. Two or three nits. Also please rebase + squash commits before merge

config_name: str | None = None
if "MCPFileAuthConfig" in scenario.effective_tags:
config_name = "mcp-file-auth"
elif "InvalidMCPFileAuthConfig" in scenario.effective_tags:
Copy link
Contributor

Choose a reason for hiding this comment

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

and one reason why not to use tags is this (basically) mapping set->one item

raise
except APIStatusError as e:
# 400 "not found": toolgroup already absent, scenario can proceed
if e.status_code == 400 and "not found" in str(e).lower():
Copy link
Contributor

Choose a reason for hiding this comment

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

prob. ok, but later 400 might be changed to names constants (from requests etc.)

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.

🧹 Nitpick comments (2)
tests/e2e/features/mcp.feature (1)

12-12: Trailing whitespace in scenario names.

Several scenario names have trailing whitespace (e.g., lines 12, 51, 86, 106, 148, 185). This can cause unnecessary diff noise in future changes.

🧹 Remove trailing whitespace
-  Scenario: Check if tools endpoint succeeds when MCP file-based auth token is passed 
+  Scenario: Check if tools endpoint succeeds when MCP file-based auth token is passed

Apply similar fix to lines 51, 86, 106, 148, and 185.

Also applies to: 51-51, 86-86, 106-106, 148-148, 185-185

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

In `@tests/e2e/features/mcp.feature` at line 12, Remove the trailing spaces at the
end of the Scenario titles so they don't introduce diff noise; specifically trim
the trailing whitespace from "Scenario: Check if tools endpoint succeeds when
MCP file-based auth token is passed " and the other Scenario lines with trailing
spaces (the Scenario names at the same positions such as the ones currently
ending with a space on lines with texts like the ones referenced in the review),
ensuring each Scenario title string has no trailing whitespace.
tests/e2e/utils/utils.py (1)

266-276: Consider adding debug logging for observability.

The function silently ignores all non-timeout failures due to check=False without any logging. While this is reasonable (e.g., ~/.llama may not exist yet), the complete lack of feedback can make E2E test debugging harder.

💡 Optional: Add minimal logging for debugging
     try:
-        subprocess.run(
+        result = subprocess.run(
             ["docker", "exec", container_name, "sh", "-c", "rm -rf ~/.llama"],
             capture_output=True,
             text=True,
             timeout=10,
             check=False,
         )
+        if result.returncode != 0:
+            print(f"Note: clear_llama_stack_storage returned {result.returncode} (may be expected if directory doesn't exist)")
     except subprocess.TimeoutExpired as e:
         print(f"Failed to clear Llama Stack storage: {e}")
         raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/utils/utils.py` around lines 266 - 276, The subprocess.run call
that removes ~/.llama (inside the try block using container_name) uses
check=False so non-timeout failures are silent; capture the CompletedProcess
result (assign subprocess.run(...) to a variable) and if result.returncode != 0
log a debug/info message including container_name, the command run, and
result.stderr/result.stdout (or at least returncode) so failed removals are
observable while still not raising; keep the existing TimeoutExpired except
branch unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/e2e/features/mcp.feature`:
- Line 12: Remove the trailing spaces at the end of the Scenario titles so they
don't introduce diff noise; specifically trim the trailing whitespace from
"Scenario: Check if tools endpoint succeeds when MCP file-based auth token is
passed " and the other Scenario lines with trailing spaces (the Scenario names
at the same positions such as the ones currently ending with a space on lines
with texts like the ones referenced in the review), ensuring each Scenario title
string has no trailing whitespace.

In `@tests/e2e/utils/utils.py`:
- Around line 266-276: The subprocess.run call that removes ~/.llama (inside the
try block using container_name) uses check=False so non-timeout failures are
silent; capture the CompletedProcess result (assign subprocess.run(...) to a
variable) and if result.returncode != 0 log a debug/info message including
container_name, the command run, and result.stderr/result.stdout (or at least
returncode) so failed removals are observable while still not raising; keep the
existing TimeoutExpired except branch unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 11b9840f-0d97-4f06-9634-2227f5fed4d1

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4f1f0 and 5e1c59e.

📒 Files selected for processing (3)
  • tests/e2e/features/environment.py
  • tests/e2e/features/mcp.feature
  • tests/e2e/utils/utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/features/environment.py

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

🧹 Nitpick comments (1)
tests/e2e/features/environment.py (1)

229-248: Consider simplifying tag-to-config mapping with a dictionary.

The if/elif chain could be reduced to a dictionary lookup for better maintainability, though the current implementation is functionally correct.

♻️ Optional refactor using dictionary lookup
-    config_name: Optional[str] = None
-    if "MCPFileAuthConfig" in scenario.effective_tags:
-        config_name = "mcp-file-auth"
-    elif "InvalidMCPFileAuthConfig" in scenario.effective_tags:
-        config_name = "invalid-mcp-file-auth"
-    elif "MCPKubernetesAuthConfig" in scenario.effective_tags:
-        config_name = "mcp-kubernetes-auth"
-    elif "MCPClientAuthConfig" in scenario.effective_tags:
-        config_name = "mcp-client-auth"
-    elif "MCPOAuthAuthConfig" in scenario.effective_tags:
-        config_name = "mcp-oauth-auth"
+    _MCP_TAG_TO_CONFIG = {
+        "MCPFileAuthConfig": "mcp-file-auth",
+        "InvalidMCPFileAuthConfig": "invalid-mcp-file-auth",
+        "MCPKubernetesAuthConfig": "mcp-kubernetes-auth",
+        "MCPClientAuthConfig": "mcp-client-auth",
+        "MCPOAuthAuthConfig": "mcp-oauth-auth",
+    }
+    config_name: Optional[str] = None
+    for tag, name in _MCP_TAG_TO_CONFIG.items():
+        if tag in scenario.effective_tags:
+            config_name = name
+            break
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/environment.py` around lines 229 - 248, The if/elif chain
mapping scenario.effective_tags to config_name can be simplified by replacing it
with a dictionary lookup: build a tag->config map and set config_name =
tag_map.get(next_matching_tag) (or iterate keys to find the first tag present)
instead of the long if/elif; keep the surrounding logic that checks config_name,
calls unregister_mcp_toolgroups()/clear_llama_stack_storage(), sets
context.scenario_config via _get_config_path(config_name, mode_dir), then calls
switch_config(context.scenario_config) and
restart_container("lightspeed-stack"). Ensure you reference the existing symbols
config_name, scenario.effective_tags, _get_config_path, switch_config,
restart_container, unregister_mcp_toolgroups, clear_llama_stack_storage, and
context.scenario_config when making the change.
🤖 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/steps/common_http.py`:
- Around line 181-187: The negated step check_response_body_does_not_contain
currently checks the raw substring against context.response.text without
performing placeholder replacement; update the function to run the incoming
substring through the same placeholder resolver used by the positive variant
(e.g., call context.replace_placeholders(substring) or the project’s placeholder
helper) before asserting it is not in context.response.text, and use the
resolved value in the assertion message as well.

---

Nitpick comments:
In `@tests/e2e/features/environment.py`:
- Around line 229-248: The if/elif chain mapping scenario.effective_tags to
config_name can be simplified by replacing it with a dictionary lookup: build a
tag->config map and set config_name = tag_map.get(next_matching_tag) (or iterate
keys to find the first tag present) instead of the long if/elif; keep the
surrounding logic that checks config_name, calls
unregister_mcp_toolgroups()/clear_llama_stack_storage(), sets
context.scenario_config via _get_config_path(config_name, mode_dir), then calls
switch_config(context.scenario_config) and
restart_container("lightspeed-stack"). Ensure you reference the existing symbols
config_name, scenario.effective_tags, _get_config_path, switch_config,
restart_container, unregister_mcp_toolgroups, clear_llama_stack_storage, and
context.scenario_config when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a0586f4-7f52-4779-a794-08ebdc383d7d

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1c59e and a589492.

📒 Files selected for processing (22)
  • docker-compose.yaml
  • docs/e2e_testing.md
  • 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/mcp_file_auth.feature
  • tests/e2e/features/steps/common_http.py
  • tests/e2e/secrets/invalid-mcp-token
  • tests/e2e/test_list.txt
  • tests/e2e/utils/llama_stack_utils.py
  • tests/e2e/utils/utils.py
💤 Files with no reviewable changes (2)
  • tests/e2e/features/mcp_file_auth.feature
  • tests/e2e/test_list.txt
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/e2e/configuration/library-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-kubernetes-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-kubernetes-auth.yaml
  • tests/e2e/secrets/invalid-mcp-token
  • tests/e2e/utils/utils.py
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp.yaml

Comment on lines +181 to +187
@then("The body of the response does not contain {substring}")
def check_response_body_does_not_contain(context: Context, substring: str) -> None:
"""Check that response body does not contain a substring."""
assert context.response is not None, "Request needs to be performed first"
assert (
substring not in context.response.text
), f"The response text '{context.response.text}' contains '{substring}'"
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 | 🟡 Minor

Apply placeholder replacement in the negated body assertion.

This step checks the raw substring while the positive variant resolves placeholders first. Placeholder-based scenarios can fail incorrectly.

🐛 Proposed fix
 `@then`("The body of the response does not contain {substring}")
 def check_response_body_does_not_contain(context: Context, substring: str) -> None:
     """Check that response body does not contain a substring."""
     assert context.response is not None, "Request needs to be performed first"
+    expected = replace_placeholders(context, substring)
     assert (
-        substring not in context.response.text
-    ), f"The response text '{context.response.text}' contains '{substring}'"
+        expected not in context.response.text
+    ), f"The response text '{context.response.text}' contains '{expected}'"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/steps/common_http.py` around lines 181 - 187, The negated
step check_response_body_does_not_contain currently checks the raw substring
against context.response.text without performing placeholder replacement; update
the function to run the incoming substring through the same placeholder resolver
used by the positive variant (e.g., call context.replace_placeholders(substring)
or the project’s placeholder helper) before asserting it is not in
context.response.text, and use the resolved value in the assertion message as
well.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/e2e/utils/llama_stack_utils.py (1)

56-70: Consider reusing the client connection for efficiency.

_unregister_mcp_toolgroups_async creates a client, then calls _unregister_toolgroup_async for each toolgroup, which creates a new client for each unregister operation. While functionally correct, this creates multiple connections.

For test cleanup this is likely acceptable, but consider passing the client instance to avoid redundant connections if performance becomes a concern.

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

In `@tests/e2e/utils/llama_stack_utils.py` around lines 56 - 70, The current
_unregister_mcp_toolgroups_async creates a client and then calls
_unregister_toolgroup_async for each toolgroup which itself creates/closes
clients; to avoid redundant connections, modify _unregister_toolgroup_async to
accept an optional client parameter (e.g., client: Optional[Client] = None) and
use that client when provided, or create/close a client only if none was passed;
update _unregister_mcp_toolgroups_async to pass its client into
_unregister_toolgroup_async for each matching toolgroup and ensure the single
client is closed in the finally block.
🤖 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/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml`:
- Around line 22-25: Create the missing Prow config file
tests/e2e-prow/rhoai/configs/lightspeed-stack-invalid-mcp-file-auth.yaml by
mirroring the local
tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml
so environment.py (line referenced at environment.py:65) can find it; then
update pipeline.sh to mount the invalid-mcp-token fixture into the test
container following the existing pattern used for mcp-mock-server-script and
other dynamic ConfigMaps (ensure the secret name/path matches the Docker mount
./tests/e2e/secrets/invalid-mcp-token:/tmp/invalid-mcp-secret-token:ro and that
any ConfigMap/secret creation logic mirrors how mcp-mock-server-script is
wired).

In `@tests/e2e/features/mcp.feature`:
- Around line 52-107: The tests show a generic "MCP server ... requires OAuth"
cause produced by check_mcp_auth() that treats any 401 as OAuth; update
check_mcp_auth() to build an auth-type-specific cause string (e.g., "requires
valid Bearer token" for file-based, "requires valid Kubernetes token" for
kubernetes, "requires valid client-provided token" for client-provided) by
reading the configured MCP auth type (or header source) and returning that
specific cause, and update the corresponding test expectations in mcp.feature
scenarios to match the new auth-specific message.

In `@tests/e2e/features/steps/place_holder.py`:
- Around line 7-20: The step function place_holder_set_mcp_server_header is a
no-op which leaves scenarios unverified; update it to either document that it's
declarative-only or actively assert the runtime config: locate
place_holder_set_mcp_server_header in tests/e2e/features/steps/place_holder.py
and either add a short comment stating the step is declarative-only and relies
on the before_scenario hooks (e.g., MCPFileAuthConfig in environment.py), or add
an assertion that the current MCP server header (read from the test context or
the active config object the hooks set) equals header_value for header_name,
failing the step if it does not—this ensures scenarios actually validate the
header state rather than silently doing nothing.

---

Nitpick comments:
In `@tests/e2e/utils/llama_stack_utils.py`:
- Around line 56-70: The current _unregister_mcp_toolgroups_async creates a
client and then calls _unregister_toolgroup_async for each toolgroup which
itself creates/closes clients; to avoid redundant connections, modify
_unregister_toolgroup_async to accept an optional client parameter (e.g.,
client: Optional[Client] = None) and use that client when provided, or
create/close a client only if none was passed; update
_unregister_mcp_toolgroups_async to pass its client into
_unregister_toolgroup_async for each matching toolgroup and ensure the single
client is closed in the finally block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a634f4e6-65d7-4a6d-a733-1ca2f85f8cae

📥 Commits

Reviewing files that changed from the base of the PR and between a589492 and c61aac9.

📒 Files selected for processing (23)
  • docker-compose.yaml
  • docs/e2e_testing.md
  • 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/mcp_file_auth.feature
  • tests/e2e/features/steps/common_http.py
  • tests/e2e/features/steps/place_holder.py
  • tests/e2e/secrets/invalid-mcp-token
  • tests/e2e/test_list.txt
  • tests/e2e/utils/llama_stack_utils.py
  • tests/e2e/utils/utils.py
💤 Files with no reviewable changes (2)
  • tests/e2e/test_list.txt
  • tests/e2e/features/mcp_file_auth.feature
🚧 Files skipped from review as they are similar to previous changes (8)
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-client-auth.yaml
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-kubernetes-auth.yaml
  • tests/e2e/configuration/library-mode/lightspeed-stack-mcp-oauth-auth.yaml
  • docker-compose.yaml
  • tests/e2e/features/steps/common_http.py
  • tests/e2e/configuration/server-mode/lightspeed-stack-mcp-oauth-auth.yaml
  • tests/e2e/utils/utils.py
  • tests/e2e/configuration/library-mode/lightspeed-stack-invalid-mcp-file-auth.yaml

Comment on lines +22 to +25
- name: "mcp-file"
url: "http://mock-mcp:3001"
authorization_headers:
Authorization: "/tmp/invalid-mcp-secret-token"
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Fixture + in-container path references:"
rg -n -C2 'invalid-mcp-token|/tmp/invalid-mcp-secret-token' . || true

echo
echo "Scenario/config wiring:"
rg -n -C2 'InvalidMCPFileAuthConfig|invalid-mcp-file-auth' tests/e2e/features/environment.py tests/e2e/features tests/e2e-prow/rhoai || true

Repository: lightspeed-core/lightspeed-stack

Length of output: 6837


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check if Prow config file exists ==="
fd -t f 'lightspeed-stack-invalid-mcp-file-auth' tests/e2e-prow/ || echo "Not found"

echo
echo "=== Check for fixture file ==="
ls -la tests/e2e/secrets/invalid-mcp-token 2>/dev/null || echo "Fixture file not found"

echo
echo "=== Pipeline.sh references to invalid-mcp or secret mounting ==="
rg -n 'invalid-mcp|create.*configmap.*secret|mount.*secret' tests/e2e-prow/ || true

Repository: lightspeed-core/lightspeed-stack

Length of output: 297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check for mcp-file-auth Prow config (should exist) ==="
ls -la tests/e2e-prow/rhoai/configs/lightspeed-stack-mcp-file-auth.yaml 2>/dev/null || echo "Not found"

echo
echo "=== Check pipeline.sh for mcp-token / mcp-secret handling ==="
rg -n -A3 'mcp-token|mcp-secret' tests/e2e-prow/rhoai/pipeline.sh || echo "Not found"

echo
echo "=== List all config files in Prow configs dir ==="
ls -1 tests/e2e-prow/rhoai/configs/ | grep lightspeed-stack || echo "No lightspeed-stack configs"

echo
echo "=== Check if pipeline.sh has any secret/ConfigMap creation logic ==="
rg -n 'create.*configmap|oc.*apply' tests/e2e-prow/rhoai/pipeline.sh | head -20 || true

Repository: lightspeed-core/lightspeed-stack

Length of output: 1493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check environment.py config loading logic ==="
rg -n -B5 -A10 'def.*load.*config|def.*before_scenario' tests/e2e/features/environment.py | head -60 || true

echo
echo "=== Check if config paths are conditionally loaded ==="
rg -n -B2 -A2 'config_path|os.path.exists|Path.*exists' tests/e2e/features/environment.py || true

Repository: lightspeed-core/lightspeed-stack

Length of output: 2979


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check _get_config_path implementation ==="
rg -n -A15 'def _get_config_path' tests/e2e/features/environment.py

echo
echo "=== Check _CONFIG_PATHS structure ==="
rg -n -B5 -A50 '_CONFIG_PATHS' tests/e2e/features/environment.py | head -100

Repository: lightspeed-core/lightspeed-stack

Length of output: 5244


Create Prow config file and wire secret mounting in pipeline.sh.

The Docker setup correctly mounts ./tests/e2e/secrets/invalid-mcp-token:/tmp/invalid-mcp-secret-token:ro and the local config files exist, but Prow is missing two pieces: (1) the config file tests/e2e-prow/rhoai/configs/lightspeed-stack-invalid-mcp-file-auth.yaml referenced in environment.py:65 does not exist, and (2) pipeline.sh has no logic to mount or create the invalid-mcp-token fixture in the container. When scenarios are unskipped, the tests will fail in Prow with a missing file error. You need to create the Prow config file (mirroring the local one) and add secret mounting to pipeline.sh (following the pattern used for mcp-mock-server-script and other dynamic ConfigMaps).

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

In
`@tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml`
around lines 22 - 25, Create the missing Prow config file
tests/e2e-prow/rhoai/configs/lightspeed-stack-invalid-mcp-file-auth.yaml by
mirroring the local
tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml
so environment.py (line referenced at environment.py:65) can find it; then
update pipeline.sh to mount the invalid-mcp-token fixture into the test
container following the existing pattern used for mcp-mock-server-script and
other dynamic ConfigMaps (ensure the secret name/path matches the Docker mount
./tests/e2e/secrets/invalid-mcp-token:/tmp/invalid-mcp-secret-token:ro and that
any ConfigMap/secret creation logic mirrors how mcp-mock-server-script is
wired).

Comment on lines +52 to +107
@skip #TODO: LCORE-1461
@InvalidMCPFileAuthConfig
Scenario: Check if tools endpoint reports error when MCP file-based invalid auth token is passed
Given The system is in default state
And The "mcp-file" mcp server "Authorization" header is set to "/tmp/invalid-mcp-secret-token"
When I access REST API endpoint "tools" using HTTP GET method
Then The status code of the response is 401
And The body of the response is the following
"""
{
"detail": {
"response": "Missing or invalid credentials provided by client",
"cause": "MCP server at http://mock-mcp:3001 requires OAuth"
}
}
"""

@skip #TODO: LCORE-1463
@InvalidMCPFileAuthConfig
Scenario: Check if query endpoint reports error when MCP file-based invalid auth token is passed
Given The system is in default state
And The "mcp-file" mcp server "Authorization" header is set to "/tmp/invalid-mcp-secret-token"
When I use "query" to ask question
"""
{"query": "Say hello", "model": "{MODEL}", "provider": "{PROVIDER}"}
"""
Then The status code of the response is 401
And The body of the response is the following
"""
{
"detail": {
"response": "Missing or invalid credentials provided by client",
"cause": "MCP server at http://mock-mcp:3001 requires OAuth"
}
}
"""

@skip #TODO: LCORE-1463
@InvalidMCPFileAuthConfig
Scenario: Check if streaming_query endpoint reports error when MCP file-based invalid auth token is passed
Given The system is in default state
And The "mcp-file" mcp server "Authorization" header is set to "/tmp/invalid-mcp-secret-token"
When I use "streaming_query" to ask question
"""
{"query": "Say hello", "model": "{MODEL}", "provider": "{PROVIDER}"}
"""
Then The status code of the response is 401
And The body of the response is the following
"""
{
"detail": {
"response": "Missing or invalid credentials provided by client",
"cause": "MCP server at http://mock-mcp:3001 requires OAuth"
}
}
"""
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if mock MCP server returns different errors per auth type
rg -n "requires OAuth" tests/

Repository: lightspeed-core/lightspeed-stack

Length of output: 3301


🏁 Script executed:

# Get broader context: find all auth-related scenarios and their expected errors
rg -B 5 "MCP server at http://mock-mcp:3001 requires OAuth" tests/e2e/features/mcp.feature | head -100

Repository: lightspeed-core/lightspeed-stack

Length of output: 3675


🏁 Script executed:

# Check if there are any different error messages for auth failures
rg -n "InvalidMCP|cause" tests/e2e/features/mcp.feature | head -50

Repository: lightspeed-core/lightspeed-stack

Length of output: 1381


🏁 Script executed:

# Extract scenario headers with their line numbers to see auth type patterns
rg -n "Scenario:|@.*Auth|@.*MCP" tests/e2e/features/mcp.feature | grep -E "(Scenario|Auth)" | head -40

Repository: lightspeed-core/lightspeed-stack

Length of output: 2686


🏁 Script executed:

# Check what tags/sections exist around the problematic lines
sed -n '45,75p' tests/e2e/features/mcp.feature
sed -n '150,175p' tests/e2e/features/mcp.feature
sed -n '310,330p' tests/e2e/features/mcp.feature

Repository: lightspeed-core/lightspeed-stack

Length of output: 3258


🏁 Script executed:

# Look for mock MCP server implementation or configuration
find . -name "*mock*mcp*" -type f | head -10

Repository: lightspeed-core/lightspeed-stack

Length of output: 127


🏁 Script executed:

# Search for any comments or documentation about error messages
rg -n "requires OAuth|mock.*server|Mock.*MCP" --type md --type txt --type py | grep -i "error\|auth" | head -20

Repository: lightspeed-core/lightspeed-stack

Length of output: 2412


🏁 Script executed:

# Check if there are step definitions that explain this behavior
rg -n "Missing or invalid credentials" tests/ -A 3 -B 3 | head -50

Repository: lightspeed-core/lightspeed-stack

Length of output: 4049


🏁 Script executed:

# Examine the mcp_oauth_probe.py file that seems to be the source
cat -n src/utils/mcp_oauth_probe.py

Repository: lightspeed-core/lightspeed-stack

Length of output: 4674


🏁 Script executed:

# Check step definitions to understand how test scenarios work
fd "step.*impl\|steps" tests/e2e --type f | head -10

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Look for test step implementations that handle "mcp-file", "mcp-kubernetes", etc
rg -l "mcp-file\|mcp-kubernetes\|mcp-client" tests/e2e --type py | head -5

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Check the mock MCP server implementation
cat -n tests/e2e/mock_mcp_server/server.py

Repository: lightspeed-core/lightspeed-stack

Length of output: 5201


🏁 Script executed:

# Check the test configuration that sets up mcp-file, mcp-kubernetes, etc.
rg -n "mcp-file\|mcp-kubernetes\|mcp-client" tests/e2e -B 2 -A 2 | head -60

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Check configuration files to see how mcp servers are configured for tests
rg -n "mcp-file\|mcp-kubernetes\|mcp-client" tests/ --type yaml --type json | head -30

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Look for conftest or fixture files that set up the test environments
fd "conftest\|fixture" tests/e2e --type py | head -5

Repository: lightspeed-core/lightspeed-stack

Length of output: 245


🏁 Script executed:

# Check if there are any comments in the feature file that explain the auth behavior
head -20 tests/e2e/features/mcp.feature

Repository: lightspeed-core/lightspeed-stack

Length of output: 706


🏁 Script executed:

# Check if there are explanations in integration or unit tests about this behavior
rg -B 5 "requires OAuth" tests/unit/utils/ tests/integration/

Repository: lightspeed-core/lightspeed-stack

Length of output: 5404


🏁 Script executed:

# Confirm the pattern: look at how authorization headers are set for different auth types
rg -n "Authorization.*header.*set\|mcp.*server.*Authorization" tests/e2e/features/mcp.feature | head -20

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Look at the step definition files to understand how test scenarios configure auth
find tests/e2e -name "*.py" -type f | head -10

Repository: lightspeed-core/lightspeed-stack

Length of output: 422


🏁 Script executed:

# Check how the authorization headers are actually set in the tests
rg -A 5 "mcp server.*Authorization.*header.*set" tests/e2e

Repository: lightspeed-core/lightspeed-stack

Length of output: 14118


🏁 Script executed:

# Check if there's any differentiation between auth types in the step implementation
cat tests/e2e/features/steps/common_http.py | grep -A 20 "mcp server.*Authorization"

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Look for any TODOs or comments related to OAuth probing
rg -n "TODO|FIXME|XXX" src/utils/mcp_oauth_probe.py

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Search for the step implementation that handles "mcp server Authorization header is set"
rg -n "mcp server.*Authorization" tests/e2e/features/steps/ -A 10

Repository: lightspeed-core/lightspeed-stack

Length of output: 58


🏁 Script executed:

# Check the environment or setup files for test configuration
cat tests/e2e/features/environment.py | head -80

Repository: lightspeed-core/lightspeed-stack

Length of output: 2881


🏁 Script executed:

# Check the configuration files to understand how different auth types are configured
ls tests/e2e/configuration/*/lightspeed-stack-*mcp*auth*.yaml

Repository: lightspeed-core/lightspeed-stack

Length of output: 834


🏁 Script executed:

# Look at one of these config files to understand the MCP server setup
cat tests/e2e/configuration/docker/lightspeed-stack-invalid-mcp-file-auth.yaml | head -100

Repository: lightspeed-core/lightspeed-stack

Length of output: 182


🏁 Script executed:

# Check the server-mode config file
cat tests/e2e/configuration/server-mode/lightspeed-stack-invalid-mcp-file-auth.yaml

Repository: lightspeed-core/lightspeed-stack

Length of output: 714


🏁 Script executed:

# Also check the oauth config to see if there's any difference
cat tests/e2e/configuration/server-mode/lightspeed-stack-mcp-oauth-auth.yaml

Repository: lightspeed-core/lightspeed-stack

Length of output: 691


🏁 Script executed:

# Search for any related GitHub issues or TODO comments
rg -n "LCORE-1461|LCORE-1463" tests/e2e/features/mcp.feature

Repository: lightspeed-core/lightspeed-stack

Length of output: 311


🏁 Script executed:

# Check what these LCORE tickets reference in the skip markers
rg -B 10 "LCORE-1461|LCORE-1463" tests/e2e/features/mcp.feature | head -40

Repository: lightspeed-core/lightspeed-stack

Length of output: 1130


Clarify that generic "requires OAuth" error applies to all MCP auth types, not just file-based.

The "MCP server at http://mock-mcp:3001 requires OAuth" error message appears consistently across all invalid token tests for file-based, kubernetes, and client-provided auth types. This is intentional—the check_mcp_auth() function uses a generic OAuth probe mechanism that treats any 401 response as OAuth requirement, regardless of the configured auth type.

Since these scenarios are currently skipped (@skip #TODO: LCORE-1461/1463), when they are enabled, consider whether the generic error message should be auth-specific (e.g., "requires valid Bearer token" for file-based, "requires valid Kubernetes token" for kubernetes auth), or if maintaining a uniform error message for all auth types is acceptable.

🤖 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 52 - 107, The tests show a
generic "MCP server ... requires OAuth" cause produced by check_mcp_auth() that
treats any 401 as OAuth; update check_mcp_auth() to build an auth-type-specific
cause string (e.g., "requires valid Bearer token" for file-based, "requires
valid Kubernetes token" for kubernetes, "requires valid client-provided token"
for client-provided) by reading the configured MCP auth type (or header source)
and returning that specific cause, and update the corresponding test
expectations in mcp.feature scenarios to match the new auth-specific message.

Comment on lines +7 to +20
@given(
'The "{mcp_server}" mcp server "{header_name}" header is set to "{header_value}"'
)
def place_holder_set_mcp_server_header(
context: Context, mcp_server: str, header_name: str, header_value: str
) -> None:
"""Set a custom Authorization header value.

Parameters:
mcp_server (str): The name of the MCP server.
header_name (str): The name of the header to set.
header_value (str): The value to set for the header.
"""
pass
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 | 🟡 Minor

Placeholder step performs no action — tests may not validate intended behavior.

This step implementation is a no-op (pass), meaning scenarios that use it don't actually configure the MCP server header at runtime. The configuration appears to be handled by the @MCPFileAuthConfig (and similar) tags via before_scenario hooks in environment.py, which switch entire config files.

If the intent is for this step to be purely declarative (documenting expected config state), consider either:

  1. Adding a comment explaining it's declarative-only
  2. Adding an assertion that verifies the config state matches expectations

Otherwise, tests may pass without actually validating the specific header configuration claimed in the scenario.

💡 Suggested documentation or verification
 `@given`(
     'The "{mcp_server}" mcp server "{header_name}" header is set to "{header_value}"'
 )
 def place_holder_set_mcp_server_header(
     context: Context, mcp_server: str, header_name: str, header_value: str
 ) -> None:
     """Set a custom Authorization header value.
+
+    Note: This is a declarative step. The actual MCP configuration is applied
+    via `@MCP`*AuthConfig scenario tags in before_scenario hooks (environment.py).
+    This step documents the expected configuration state for readability.

     Parameters:
         mcp_server (str): The name of the MCP server.
         header_name (str): The name of the header to set.
         header_value (str): The value to set for the header.
     """
     pass
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/features/steps/place_holder.py` around lines 7 - 20, The step
function place_holder_set_mcp_server_header is a no-op which leaves scenarios
unverified; update it to either document that it's declarative-only or actively
assert the runtime config: locate place_holder_set_mcp_server_header in
tests/e2e/features/steps/place_holder.py and either add a short comment stating
the step is declarative-only and relies on the before_scenario hooks (e.g.,
MCPFileAuthConfig in environment.py), or add an assertion that the current MCP
server header (read from the test context or the active config object the hooks
set) equals header_value for header_name, failing the step if it does not—this
ensures scenarios actually validate the header state rather than silently doing
nothing.

@tisnik tisnik requested a review from radofuchs March 18, 2026 08:00
@jrobertboos jrobertboos force-pushed the lcore-1329 branch 2 times, most recently from f2a3b0c to b0a51bb Compare March 18, 2026 15:19
Copy link
Contributor

@radofuchs radofuchs left a comment

Choose a reason for hiding this comment

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

LGTM

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

Add invalid MCP token configuration and update tests

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

Add additional MCP authentication configurations and update scenario 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.

fixed errored tests

skipped failing scenarios and added library mode configs

fixed library mode tests error

fixed black

skipped acidentally missed failing test

Refactor Llama Stack utilities and update E2E tests

- Renamed `llama_stack_shields.py` to `llama_stack_utils.py` and expanded its functionality to manage both toolgroups and shields.
- Removed the deprecated `llama_stack_tools.py` file.
- Updated E2E test scenarios to utilize the new utility functions for unregistering toolgroups and clearing Llama Stack storage.
- Enhanced feature files to include comments indicating pending fixes for skipped scenarios.

Enhance MCP feature tests and streamline Llama Stack storage clearing

- Added checks to ensure the response body does not contain 'mcp-client' in MCP feature tests.
- Simplified the `clear_llama_stack_storage` function to remove the entire `~/.llama` directory instead of specific files, improving clarity and efficiency.
- Updated comments to reflect the changes in storage clearing logic.

Remove deprecated MCP file-based authorization feature and update test list

addressed code rabbit

Add new MCP authentication configurations and update tests

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

fixed errored tests

fixed library mode tests error

fixed black

Refactor Llama Stack utilities and update E2E tests

- Renamed `llama_stack_shields.py` to `llama_stack_utils.py` and expanded its functionality to manage both toolgroups and shields.
- Removed the deprecated `llama_stack_tools.py` file.
- Updated E2E test scenarios to utilize the new utility functions for unregistering toolgroups and clearing Llama Stack storage.
- Enhanced feature files to include comments indicating pending fixes for skipped scenarios.

Enhance MCP feature tests and streamline Llama Stack storage clearing

- Added checks to ensure the response body does not contain 'mcp-client' in MCP feature tests.
- Simplified the `clear_llama_stack_storage` function to remove the entire `~/.llama` directory instead of specific files, improving clarity and efficiency.
- Updated comments to reflect the changes in storage clearing logic.

Remove deprecated MCP file-based authorization feature and update test list

addressed code rabbit

fixed ruff

addressed comments

addressed talk with radovan

addressed comments

fixed black

fixed error
Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

LGTM

@tisnik tisnik merged commit c6a2205 into lightspeed-core:main Mar 18, 2026
21 of 24 checks passed
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.

3 participants