Skip to content

Add RelayShield security tools (MCP registry risk + prompt-injection breach detection)#6550

Open
nzdsf2-gif wants to merge 3 commits into
crewAIInc:mainfrom
nzdsf2-gif:feature/relayshield-tool
Open

Add RelayShield security tools (MCP registry risk + prompt-injection breach detection)#6550
nzdsf2-gif wants to merge 3 commits into
crewAIInc:mainfrom
nzdsf2-gif:feature/relayshield-tool

Conversation

@nzdsf2-gif

Copy link
Copy Markdown

Adds two tools for agent-specific security checks: mcp-registry-risk (typosquat/reputation scoring for MCP servers and tool registries) and prompt-injection-breach (credential breach exposure sourced from prompt-injection attacks against agents). Both call RelayShield's live API — no API key required via the x402 PAYG route (pay-per-call in USDC, no signup), or an API key for the metered route. Docs/usage example included in the tool's README following the existing pattern.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f057e3a0-53a6-45d1-8163-6eefbdb7d383

📥 Commits

Reviewing files that changed from the base of the PR and between fd68030 and dd3ac7b.

📒 Files selected for processing (1)
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py

📝 Walkthrough

Walkthrough

RelayShield integrations add typed inputs and authenticated API calls for MCP registry risk and prompt-injection breach checks, with formatted results, public package exports, and setup documentation.

Changes

RelayShield security tools

Layer / File(s) Summary
Tool contracts and API configuration
lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/schemas.py, lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py
Adds typed parameters for MCP and email checks, plus shared RelayShield API configuration and authentication headers.
Risk-check workflows
lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py
Adds API-backed tools for MCP registry risk and prompt-injection breach checks, including validation, request errors, and formatted responses.
Public exports and usage documentation
lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/__init__.py, lib/crewai-tools/src/crewai_tools/__init__.py, lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/README.md
Exports both tools through package namespaces and documents API-key setup and example usage.

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant RelayShieldTool
  participant RelayShieldAPI
  Agent->>RelayShieldTool: provide MCP identifier or email
  RelayShieldTool->>RelayShieldAPI: send authenticated risk request
  RelayShieldAPI-->>RelayShieldTool: return wrapped assessment
  RelayShieldTool-->>Agent: return formatted verdict or exposure result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the addition of RelayShield security tools for MCP risk and prompt-injection breach detection.
Description check ✅ Passed The description is directly related to the added RelayShield tools, documentation, and API usage pattern.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py`:
- Around line 2-13: Remove the ClassVar import and its annotation from the
module-level API_BASE_URL constant. Keep API_BASE_URL typed as a regular
module-level string constant.
- Around line 16-26: The keyless x402 route must work without
RELAYSHIELD_API_KEY. In relayshield_tool.py lines 16-26, update
_relayshield_headers to omit X-RS-API-KEY when no key is configured instead of
raising ValueError; also set env_vars required=False at lines 41-47 and 96-102
so both tool configurations permit keyless use.
- Around line 59-71: The RelayShield tools leave response JSON parsing outside
their request error handling, allowing invalid 2XX responses to crash. In
lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py
lines 59-71, update RelayShieldMCPRiskTool to parse the response and extract
data inside the existing try block; apply the same change to
RelayShieldPromptInjectionBreachTool at lines 105-117, preserving the existing
failure-string handling for parsing errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5365f4f2-4c24-4950-bd27-d9131f8bff99

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5d0ec and ee4c62a.

📒 Files selected for processing (5)
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/README.md
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/schemas.py

Comment thread lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py Outdated
Comment on lines +16 to +26
def _relayshield_headers() -> dict[str, str]:
api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
if not api_key:
raise ValueError(
"RELAYSHIELD_API_KEY environment variable is required. "
"Get a key at https://api.relayshield.net/developers"
)
# Note: relayshield_agentic_api.py (which hosts these two endpoints)
# expects X-RS-API-KEY specifically, not the X-API-Key convention used
# by RelayShield's other metered endpoints.
return {"Content-Type": "application/json", "X-RS-API-KEY": api_key}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

API key requirement breaks the keyless x402 pay-per-call route. The PR objectives and documentation state that the tools support an x402 route requiring no API key. However, the code strictly enforces the presence of RELAYSHIELD_API_KEY, which will block users attempting to use the keyless payment route.

  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L16-L26: Remove the ValueError when api_key is empty, and instead omit the X-RS-API-KEY header if a key isn't provided.
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L41-L47: Change required=True to required=False in the env_vars declaration.
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L96-L102: Change required=True to required=False in the env_vars declaration.
🛠️ Proposed fix for `_relayshield_headers`
 def _relayshield_headers() -> dict[str, str]:
+    headers = {"Content-Type": "application/json"}
     api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
-    if not api_key:
-        raise ValueError(
-            "RELAYSHIELD_API_KEY environment variable is required. "
-            "Get a key at https://api.relayshield.net/developers"
-        )
-    # Note: relayshield_agentic_api.py (which hosts these two endpoints)
-    # expects X-RS-API-KEY specifically, not the X-API-Key convention used
-    # by RelayShield's other metered endpoints.
-    return {"Content-Type": "application/json", "X-RS-API-KEY": api_key}
+    if api_key:
+        headers["X-RS-API-KEY"] = api_key
+    return headers
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _relayshield_headers() -> dict[str, str]:
api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
if not api_key:
raise ValueError(
"RELAYSHIELD_API_KEY environment variable is required. "
"Get a key at https://api.relayshield.net/developers"
)
# Note: relayshield_agentic_api.py (which hosts these two endpoints)
# expects X-RS-API-KEY specifically, not the X-API-Key convention used
# by RelayShield's other metered endpoints.
return {"Content-Type": "application/json", "X-RS-API-KEY": api_key}
def _relayshield_headers() -> dict[str, str]:
headers = {"Content-Type": "application/json"}
api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
if api_key:
headers["X-RS-API-KEY"] = api_key
return headers
📍 Affects 1 file
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L16-L26 (this comment)
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L41-L47
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L96-L102
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py`
around lines 16 - 26, The keyless x402 route must work without
RELAYSHIELD_API_KEY. In relayshield_tool.py lines 16-26, update
_relayshield_headers to omit X-RS-API-KEY when no key is configured instead of
raising ValueError; also set env_vars required=False at lines 41-47 and 96-102
so both tool configurations permit keyless use.

Comment thread lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py Outdated
…-key crash

- Remove ClassVar from module-level API_BASE_URL (only valid in class bodies)
- Move resp.json() parsing inside the try block so an invalid-JSON 2XX
  response is caught as a RequestException instead of crashing
- Check for RELAYSHIELD_API_KEY before the request and return a clean error
  string instead of raising an uncaught ValueError from inside the try block

Not implementing full keyless x402 client signing per the "omit the header"
suggestion — these tools only call the metered endpoint, which has no x402
signing logic behind it; omitting the header would just produce a 401, not
enable PAYG. That's a larger scope change than this PR, not a quick fix.
@nzdsf2-gif

Copy link
Copy Markdown
Author

Thanks for the review — addressed in fd68030:

  • Removed ClassVar from the module-level API_BASE_URL constant (it's only valid inside a class body).
  • Moved resp.json() parsing inside the try block for both tools, so an invalid-JSON 2XX response is caught as a RequestException instead of crashing.
  • Fixed the real bug underneath the API-key finding: _relayshield_headers() was raising a bare ValueError on a missing key from inside the try block, which the except requests.RequestException clause never caught — an uncaught exception, not a graceful error. Now checked up front in _run(), returning a clean error string instead.

Not implementing the "omit the header, allow keyless" suggestion as proposed: these tools only call the metered endpoint (/v1/metered/...), which has no x402 payment-signing logic behind it — omitting the header would just produce a 401, not actually enable the PAYG route. Real x402 client-side signing inside a CrewAI tool is a larger scope change than this PR; happy to take that on as a follow-up if there's interest.

Smoke-tested all three cases (successful call, breach check, missing-key error path) against the live API before pushing.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py`:
- Around line 106-111: Add the missing os module import to the module containing
the RelayShield tool before its use in the API-key validation flow, so the
existing os.environ.get call works without changing the validation behavior.
- Around line 47-52: Add the missing os module import at the top of the module
containing the RelayShield tool before the api_key lookup in the tool’s
initialization or execution flow. Preserve the existing RELAYSHIELD_API_KEY
validation and error response.
- Around line 68-69: Validate that the parsed JSON is a dictionary before
calling .get() in both RelayShieldMCPRiskTool
(lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L68-L69)
and RelayShieldPromptInjectionBreachTool
(lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py#L121-L122).
For non-dictionary responses, return the tool’s existing clean error string
instead of allowing AttributeError to escape; preserve normal data extraction
for dictionary responses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 41aa096f-5bea-4422-a3a8-f05a451b9cac

📥 Commits

Reviewing files that changed from the base of the PR and between ee4c62a and fd68030.

📒 Files selected for processing (1)
  • lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py

Comment on lines +47 to +52
api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
if not api_key:
return (
"Error: RELAYSHIELD_API_KEY environment variable is required. "
"Get a key at https://api.relayshield.net/developers"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Missing os module import.

The os module is used here to read RELAYSHIELD_API_KEY, but based on the file context, it has not been imported. This will raise a NameError at runtime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py`
around lines 47 - 52, Add the missing os module import at the top of the module
containing the RelayShield tool before the api_key lookup in the tool’s
initialization or execution flow. Preserve the existing RELAYSHIELD_API_KEY
validation and error response.

Comment thread lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py Outdated
Comment on lines +106 to +111
api_key = os.environ.get("RELAYSHIELD_API_KEY", "")
if not api_key:
return (
"Error: RELAYSHIELD_API_KEY environment variable is required. "
"Get a key at https://api.relayshield.net/developers"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Missing os module import (mirrors the risk tool).

The os module is used here to read RELAYSHIELD_API_KEY, but it is not imported, which will result in a NameError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/src/crewai_tools/tools/relayshield_tool/relayshield_tool.py`
around lines 106 - 111, Add the missing os module import to the module
containing the RelayShield tool before its use in the API-key validation flow,
so the existing os.environ.get call works without changing the validation
behavior.

resp.json() could theoretically return a valid-but-non-dict JSON value
(list, string, etc.) for a 2XX response, which would crash with
AttributeError on .get() before ever reaching the RequestException handler.
Now checks isinstance(response_json, dict) before extracting data.
@nzdsf2-gif

Copy link
Copy Markdown
Author

Addressed in dd3ac7b:

  • Non-dict JSON response guard: fixed for both tools — checks isinstance(response_json, dict) before calling .get(), so a valid-but-unexpected JSON shape (list/string) returns a clean fallback instead of crashing.
  • The "missing os import" finding is a false positive — import os is present at the top of the file in both the base commit and the current one (unchanged by either of my commits). Looks like a scoped-diff artifact rather than an actual regression. Skipping, no change needed.

Re-tested both the real API call and a mocked non-dict-response case before pushing — both behave correctly now.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant