|
| 1 | +--- |
| 2 | +title: Guardrails in MCP-Scan |
| 3 | +description: Use MCP-scan to ensure your MCP servers are safe to use. |
| 4 | +icon: bootstrap/lock |
| 5 | +--- |
| 6 | + |
| 7 | +# Guardrails in `mcp-scan` |
| 8 | + |
| 9 | +<div class='subtitle'> |
| 10 | +Safeguard and restrict your MCP tool calls and responses. |
| 11 | +</div> |
| 12 | + |
| 13 | +The `mcp-scan proxy` command supports dynamic guardrailing for MCP servers, letting you enforce safety rules during tool use. It comes with a set of default guardrails and allows you to define custom behavior through a configuration file. |
| 14 | + |
| 15 | +This guide covers how to structure guardrail configs, write custom rules, and apply enforcement at the client, server, and tool levels. |
| 16 | + |
| 17 | +<img src="../assets/proxy.svg" alt="proxying-overview-diagram" class="textwidth" style="max-width: 420pt; margin: 40pt auto; display: block;" /> |
| 18 | + |
| 19 | +!!! note |
| 20 | + By default, the configuration file is located at `~/.mcp-scan/guardrails-config.yml`. |
| 21 | + |
| 22 | +## File structure |
| 23 | + |
| 24 | +The configuration file defines guardrailing behavior hierarchically, scoped by **client**, **server**, and **tool**. Below is a structured overview of the YAML format: |
| 25 | + |
| 26 | +```yaml |
| 27 | +<client-name>: |
| 28 | + <server-name>: |
| 29 | + guardrails: |
| 30 | + <default-guardrail-name>: <guardrail-action> |
| 31 | + ... |
| 32 | + |
| 33 | + custom_guardrails: |
| 34 | + - name: <guardrail-name> |
| 35 | + id: <guardrail-id> |
| 36 | + action: <guardrail-action> |
| 37 | + content: | |
| 38 | + <guardrail-content> |
| 39 | + ... |
| 40 | + |
| 41 | + tools: |
| 42 | + <tool-name>: |
| 43 | + <default-guardrail-name>: <guardrail-action> |
| 44 | + ... |
| 45 | + enabled: <boolean> |
| 46 | + ... |
| 47 | +... |
| 48 | +``` |
| 49 | + |
| 50 | +Each configuration block defines a set of **default**, **custom**, and **tool-level** guardrails for a specific `<client> / <server>` combination (e.g., a client like Cursor and a server like a Git MCP instance). |
| 51 | + |
| 52 | +The ellipses (`...`) in the schema indicate that multiple entries can be added at each level: |
| 53 | + |
| 54 | +- Multiple servers under a single client. |
| 55 | + |
| 56 | +- Multiple guardrails under each guardrails section. |
| 57 | + |
| 58 | +- Multiple tools under a server. |
| 59 | + |
| 60 | +This structure supports flexible, fine-grained control across different environments and toolchains. The sections of the file are described below. |
| 61 | + |
| 62 | + |
| 63 | +## Default guardrails |
| 64 | + |
| 65 | +Default guardrails are pre-configured and run by default with the `log` action. Alternatively, their behavior can be overridden by specifying `<default-guardrail-name>: <guardrail-action>` as shown above. The following default guardrails exist and can be configured: |
| 66 | + |
| 67 | +| Name | Requirements | Description | |
| 68 | +|-------------|--------|----------------------------------------| |
| 69 | +| `links` | `None` | Detects links. | |
| 70 | +| `secrets` | `None` | Detects secrets, such as tokens or api keys. | |
| 71 | +| `pii` | `presidio`, `transformers` | Detects personally identifiable information, such as names, emails, or credit card numbers. | |
| 72 | +| `moderated` | `ENV: OPENAI_API_KEY` | Detects content that should be moderated, such as hate speech or explicit content. | |
| 73 | + |
| 74 | + |
| 75 | +!!! note |
| 76 | + Some default guardrails require optional extra packages to run and are disabled if not installed. You can install them with the argument `--install-extras [list of extras]` to install a specific set of extras or `--install-extras all` to install all extras. |
| 77 | + |
| 78 | + |
| 79 | +**Example:** Overriding a default guardrail. |
| 80 | +```yaml |
| 81 | +cursor: |
| 82 | + email-mcp-server: |
| 83 | + guardrails: |
| 84 | + pii: block |
| 85 | + secrets: paused |
| 86 | +``` |
| 87 | +
|
| 88 | +## Custom guardrails |
| 89 | +Custom guardrails allow you to define rules tailored to specific workflows, data patterns, or business logic. These guardrails offer flexible semantics and can detect complex or domain-specific behaviors that aren't covered by the built-in defaults. |
| 90 | +
|
| 91 | +To get started writing custom rules, refer to the [rule writing reference](./rules.md) to get started quickly with writing guardrails, or explore the rest of this documentation to learn about the concepts in depth, perhaps [starting with this introduction](./index.md). |
| 92 | +
|
| 93 | +### Schema |
| 94 | +A custom guardrail is defined using the following fields: |
| 95 | +
|
| 96 | +| Name | Type | Description | |
| 97 | +|-------------|-------------|----------------------------------------| |
| 98 | +| `name` | `string` | A name for the guardrail. | |
| 99 | +| `id` | `string` | A unique identifier for the guardrail. | |
| 100 | +| `action` | `Guardrail Action` | The `action` to take when this guardrail is triggered. See below. | |
| 101 | +| `content` | `string` | A multiline string defining the semantics of this guardrailing rule. Please refer to the rest of this documentation to see how to write guardrails. | |
| 102 | + |
| 103 | +**Example:** Defining a custom guardrail. |
| 104 | + |
| 105 | +Below is a `yaml` fragment that shows how a custom guardrail can be defined to ensure emails are only sent to trusted recipients. |
| 106 | +```yaml |
| 107 | +... |
| 108 | +custom_guardrails: |
| 109 | + - name: "Trusted Recipient Email" |
| 110 | + id: "trusted_email_check" |
| 111 | + action: block |
| 112 | + content: | |
| 113 | + raise "Untrusted email recipient" if: |
| 114 | + (call: ToolCall) |
| 115 | + call is tool:send_email |
| 116 | + not match(".*@company.com", call.function.arguments.recipient) |
| 117 | +... |
| 118 | +``` |
| 119 | + |
| 120 | +## Tool-specific guardrails |
| 121 | +Tool-specific guardrails allow you to override or refine guardrailing behavior for individual tools within a given server. This is useful when certain tools require stricter enforcement, relaxed rules, or even complete disabling. |
| 122 | + |
| 123 | + |
| 124 | +### Schema |
| 125 | +The following fields are available for each `tool`: |
| 126 | + |
| 127 | +| Name | Type | Description | |
| 128 | +|-------------|-------------|----------------------------------------| |
| 129 | +| `<default-guardrail>` | `Guardrail Action` | Overrides the behavior of `<default-guardrail>` for this tool specifically. | |
| 130 | +| `enabled` | `boolean` | Disables tool when set to <span class='boolean-value-false'>False</span>. If this field is not provided, it defaults to <span class='boolean-value-true'>True</span>. | |
| 131 | + |
| 132 | + |
| 133 | +!!! note |
| 134 | + While the tools section does not allow for defining custom guardrails, you can always use the `call is tool:<tool_name>` syntax to select a specific tool, as shown in the example from the previous section. |
| 135 | + |
| 136 | +**Example:** Disabling and overriding behavior for tools. |
| 137 | + |
| 138 | +The `yaml` fragment below demonstrates how to disable the `send_message` tool and override the behavior for the `secrets` default guardrail to be `block` specifically for the `read_messages` tool. |
| 139 | +```yaml |
| 140 | +... |
| 141 | +tools: |
| 142 | + send_message: |
| 143 | + enabled: false |
| 144 | +
|
| 145 | + read_messages: |
| 146 | + secrets: block |
| 147 | +... |
| 148 | +``` |
| 149 | + |
| 150 | +## Guardrail actions |
| 151 | +Guardrail actions define how the system responds when a guardrail is triggered. These actions let you tailor enforcement severity across different contexts - whether to silently monitor behavior, stop execution entirely, or pause enforcement temporarily. |
| 152 | + |
| 153 | +The following actions are available: |
| 154 | + |
| 155 | +| Name | Description | |
| 156 | +|-------------|----------------------------------------| |
| 157 | +| `block` | This will block the client outright, preventing it from executing what triggered the guardrail. | |
| 158 | +| `log` | This lets the client execute what triggered the guardrail, but logs it in the console, so you can monitor the violation patterns without blocking the client's workflow. | |
| 159 | +| `paused` | This pauses the enforcement of the guardrail. | |
| 160 | + |
| 161 | +These actions are available for both default and custom guardrails. |
| 162 | + |
| 163 | + |
| 164 | +## Priority of rules |
| 165 | +When multiple guardrail rules are defined for the same context (e.g., a default guardrail set at both the server and tool level), the system follows a strict priority hierarchy to determine which rule takes effect. |
| 166 | + |
| 167 | +This ensures predictable behavior and allows for precise control, especially when configuring environments with overlapping scopes. |
| 168 | + |
| 169 | + |
| 170 | +!!! note |
| 171 | + **Custom guardrails are not subject to this priority system**. Once defined, they are always active and enforced with their specified action, regardless of other configurations. |
| 172 | + |
| 173 | +### Rule Priority (from highest to lowest) |
| 174 | +1. **Tool-specific guardrails** |
| 175 | + Guardrails defined for a specific tool override all other configurations. |
| 176 | +2. **Client/server-specific guardrails** |
| 177 | + Guardrails set at the server level for a particular client are used unless overridden by a tool-specific rule. |
| 178 | +3. **Implicit default guardrails** |
| 179 | + If no explicit rule is defined, the default guardrails apply automatically with the `log` action. |
| 180 | + |
| 181 | +**Example:** A simple config file with overrides. |
| 182 | + |
| 183 | +To see how this hierarchy of precedence works, consider the following example configuration: |
| 184 | + |
| 185 | +```yaml |
| 186 | +client: |
| 187 | + server: |
| 188 | + guardrails: |
| 189 | + pii: block |
| 190 | + secrets: paused |
| 191 | + |
| 192 | + tools: |
| 193 | + tool: |
| 194 | + secrets: block |
| 195 | +``` |
| 196 | + |
| 197 | +The resulting behavior of this configuration is: |
| 198 | + |
| 199 | +- `secrets` is set to block for the `tool`, because tool-specific guardrails have the highest priority. |
| 200 | + |
| 201 | +- For all other tools on `server`, secrets is set to `paused`, as defined at the client/server level. |
| 202 | + |
| 203 | +- For any other client/server combinations not defined in the config, `secrets` will use the implicit default and be set to `log`. |
| 204 | + |
| 205 | +- `pii` is set to block across all tools under `server` due to the client/server setting. For all other combinations, it falls back to the implicit default (`log`). |
| 206 | + |
| 207 | +This precedence ensures that you can enforce strict guardrails where needed, while still benefiting from broad default coverage elsewhere. |
| 208 | + |
| 209 | + |
| 210 | +## Example file |
| 211 | +Below is a complete example of a guardrail configuration file. |
| 212 | +It demonstrates how to define default and custom guardrails for specific clients and servers, apply tool-level overrides, and selectively enable or disable tools |
| 213 | + |
| 214 | +```yaml |
| 215 | +cursor: |
| 216 | + email-mcp-server: |
| 217 | +
|
| 218 | + # Customize the guardrailing for this specific server |
| 219 | + guardrails: |
| 220 | + pii: block |
| 221 | + moderated: paused |
| 222 | +
|
| 223 | + # Define multiple custom guardrails |
| 224 | + custom_guardrails: |
| 225 | + - name: "Trusted Recipient Email" |
| 226 | + id: "untrustsed_email_gr_1" |
| 227 | + action: block |
| 228 | +
|
| 229 | + # Guardrail to ensure that we know all recipients |
| 230 | + content: | |
| 231 | + raise "Untrusted email recipient" if: |
| 232 | + (call: ToolCall) |
| 233 | + call is tool:send_email |
| 234 | + not match(".*@company.com", call.function.arguments.recipient) |
| 235 | +
|
| 236 | +
|
| 237 | + # Guardrail to ensure an email is not sent after |
| 238 | + # a prompt injection is detected in the inbox |
| 239 | + - name: "PII Email" |
| 240 | + id: "untrustsed_email_gr_2" |
| 241 | + action: log |
| 242 | + content: | |
| 243 | + from invariant.detectors import prompt_injection |
| 244 | +
|
| 245 | + raise "Suspicious email before send" if: |
| 246 | + (inbox: ToolOutput) -> (call: ToolCall) |
| 247 | + inbox is tool:get_inbox |
| 248 | + call is tool:send_email |
| 249 | + prompt_injection(inbox.content) |
| 250 | +
|
| 251 | + # Specify the behavior of individual tools |
| 252 | + tools: |
| 253 | + send_message: |
| 254 | + enabled: false |
| 255 | +
|
| 256 | + read_messages: |
| 257 | + secrets: block |
| 258 | +
|
| 259 | + weather: |
| 260 | + guardrails: |
| 261 | + moderated: paused |
| 262 | +
|
| 263 | +# Separate configurations on a per client/server basis |
| 264 | +claude: |
| 265 | + git-mcp-server: |
| 266 | + tools: |
| 267 | + commit-tool: |
| 268 | + links: paused |
| 269 | +``` |
0 commit comments