Skip to main content
Glama
ShieldLabs-ai

@shieldlabs-ai/mcp

Official

Verify webhook signature

shieldlabs_verify_webhook_signature
Read-onlyIdempotent

Verify ShieldLabs webhook deliveries by checking the X-Shield-Signature header against the raw request body and endpoint signing secret, helping debug mismatches and rotated secrets.

Instructions

Check a received ShieldLabs webhook delivery: does its X-Shield-Signature header match the raw body for the endpoint signing secret? For debugging a webhook endpoint. SHIELDLABS_WEBHOOK_SECRET is not set on this server, so pass secret.

The signature is "sha256=" plus the lowercase hex HMAC-SHA256 of the raw body, keyed with the full signing secret including its whsec_ prefix. While a secret is rotated, give several secrets separated by commas (in secret or in SHIELDLABS_WEBHOOK_SECRET): the delivery is valid when any of them matches, and matched_secret tells which. When the signature does not match a body of up to 64 KB, the tool tests common body changes (re-serialized JSON, unescaped characters, added newline) and says which one explains the mismatch. It never returns a secret or an expected signature.

Args:

  • payload (string): raw body exactly as received

  • signature_header (string): value of X-Shield-Signature

  • secret (string, optional): whsec_... signing secret of the endpoint, or several separated by commas

  • payload_encoding ('utf8' | 'base64'): default 'utf8'

  • response_format ('markdown' | 'json'): default 'markdown'

Returns (json): { "valid": boolean, "secret_source": "argument" | "environment", "secrets_checked": number, "matched_secret": number | null, "checks": [{ check, ok, detail }], "event": { event_type, schema_version, created_at, event_id, identification_id, risk_score, risk_band } | null, "hint": string | null }

Input Schema

NameRequiredDescriptionDefault
secretNoEndpoint signing secret including its whsec_ prefix, or several separated by commas while you rotate. Defaults to SHIELDLABS_WEBHOOK_SECRET from the server environment.
payloadYesThe raw request body exactly as received: the bytes that were signed. Do not pretty-print, re-serialize or trim it.
response_formatNoOutput format: 'markdown' (default) for readable text, 'json' for structured datamarkdown
payload_encodingNo'utf8' (default) when payload is the body text; 'base64' when payload is the body bytes encoded as base64 (use it when exact bytes such as a trailing newline matter)utf8
signature_headerYesValue of the X-Shield-Signature header, for example "sha256=ea26857...fdd8"

Output Schema

NameRequiredDescriptionDefault
hintYes
eventYes
validYes
checksYes
secret_sourceYes
matched_secretYesPosition, from 1, of the secret that matches, in the order given; null when none
secrets_checkedYesHow many signing secrets were checked (several while a secret is rotated)

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv1.1.0
    • changedOutput schema / properties / event / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "created_at": {
      -        "type": "string"
      -      },
      -      "event_type": {
      -        "type": "string"
      -      },
      -      "request_id": {
      -        "anyOf": [
      -          {
      -            "description": "Request ID of an identification.scored event",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "risk_band": {
      -        "anyOf": [
      -          {
      -            "enum": [
      -              "trusted",
      -              "suspicious",
      -              "dangerous",
      -              "rate_limited"
      -            ],
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "risk_score": {
      -        "anyOf": [
      -          {
      -            "maximum": 9007199254740991,
      -            "minimum": -9007199254740991,
      -            "type": "integer"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "schema_version": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "event_type",
      -      "schema_version",
      -      "created_at",
      -      "request_id",
      -      "risk_score",
      -      "risk_band"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "created_at": {
      +        "type": "string"
      +      },
      +      "event_id": {
      +        "anyOf": [
      +          {
      +            "description": "Signed event_id when the delivery includes one",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "event_type": {
      +        "type": "string"
      +      },
      +      "identification_id": {
      +        "anyOf": [
      +          {
      +            "description": "Identification ID of an identification.scored event.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "risk_band": {
      +        "anyOf": [
      +          {
      +            "enum": [
      +              "trusted",
      +              "suspicious",
      +              "dangerous",
      +              "banned_ip"
      +            ],
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "risk_score": {
      +        "anyOf": [
      +          {
      +            "maximum": 9007199254740991,
      +            "minimum": -9007199254740991,
      +            "type": "integer"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "schema_version": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "event_type",
      +      "schema_version",
      +      "created_at",
      +      "event_id",
      +      "identification_id",
      +      "risk_score",
      +      "risk_band"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
  2. First observedv1.0.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the readOnly/destructive hints by explaining technical constraints (64 KB body limit), the underlying cryptographic process (sha256+ hex HMAC-SHA256), and critical failure modes—specifically that it 'never returns a secret or an expected signature.' It even mentions active error mitigation (testing for re-serialized JSON changes).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is necessarily detailed due to the complex technical requirements (signature format, secret rotation, error testing), making it long but highly functional. It prioritizes the core purpose and constraints upfront.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity and the presence of a rich output schema, the description is extremely complete. It details the security mechanisms, the logic of multi-secret checking, and the error-handling capabilities of the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While the schema coverage is 100% (baseline 3), the description adds essential operational context that the schema misses. It clarifies that the `secret` can be a comma-separated list for rotation, and it specifies required formats for `payload` (no pretty-printing).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly defines the function as checking if an X-Shield-Signature header matches the raw body, specifically for 'debugging a webhook endpoint.' This is highly specific and immediately distinguishes it from the list or get operations of sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit guidance on when to use the tool (debugging) and how to handle complexity, such as providing multiple rotating secrets separated by commas. It also notes environmental constraints ('SHIELDLABS_WEBHOOK_SECRET is not set on this server').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.