BerriAI/litellm · error · ValueError

MCPJWTSigner guardrail '{guardrail_name}' has mode='{mode}'

Error message

MCPJWTSigner guardrail '{guardrail_name}' has mode='{mode}' but must use mode='pre_mcp_call'. JWT injection only fires for MCP tool calls.

What it means

The MCP JWT signer only makes sense in front of MCP tool calls, so initialize_guardrail() hard-requires litellm_params.mode == 'pre_mcp_call' and raises ValueError otherwise. mode selects the guardrail event hook, and JWT injection into MCP authorization headers only fires on that hook.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/__init__.py:22

from litellm.types.guardrails import SupportedGuardrailIntegrations

from .mcp_jwt_signer import MCPJWTSigner, get_mcp_jwt_signer

if TYPE_CHECKING:
    from litellm.types.guardrails import Guardrail, LitellmParams


def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail") -> MCPJWTSigner:
    import litellm

    guardrail_name: Final = guardrail.get("guardrail_name")
    if not guardrail_name:
        raise ValueError("MCPJWTSigner guardrail requires a guardrail_name")

    mode: Final = litellm_params.mode
    if mode != "pre_mcp_call":
        raise ValueError(
            f"MCPJWTSigner guardrail '{guardrail_name}' has mode='{mode}' but must use "
            "mode='pre_mcp_call'. JWT injection only fires for MCP tool calls."
        )

    optional_params: Final = getattr(litellm_params, "optional_params", None)

    def _get(key):
        if optional_params is not None:
            v: Final = getattr(optional_params, key, None)
            if v is not None:
                return v
        return getattr(litellm_params, key, None)

    signer: Final = MCPJWTSigner(
        guardrail_name=guardrail_name,
        event_hook=litellm_params.mode,
        default_on=litellm_params.default_on,
        # Core signing

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Set mode: pre_mcp_call in the guardrail's litellm_params
  2. If you did not intend JWT injection for MCP calls, remove the guardrail instead of changing its mode

Example fix

# before
 litellm_params:
   mode: pre_call

# after
 litellm_params:
   mode: pre_mcp_call
Defensive patterns

Strategy: validation

Validate before calling

lp = guardrail_entry["litellm_params"]  
assert lp.get("mode") == "pre_mcp_call", (  
    f"mcp_jwt_signer requires mode='pre_mcp_call', got {lp.get('mode')!r}"  
)

Type guard

def is_pre_mcp_call_mode(lp: object) -> bool:  
    return isinstance(lp, dict) and lp.get("mode") == "pre_mcp_call"

Prevention

When it happens

Trigger: An mcp_jwt_signer guardrail entry with mode set to another hook such as pre_call, during_call, or post_call - or mode omitted so it resolves to a non-matching value.

Common situations: Configs copied from generic guardrail examples that use mode: pre_call; mode keys typo'd; guardrail entries reused across hooks during refactors.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/e6f3844f8c32cb5f. Report an issue: GitHub.