Skip to content

Commit ff39491

Browse files
committed
bump to 0.20.3
1 parent 03b382d commit ff39491

13 files changed

Lines changed: 1238 additions & 15 deletions

docs/development/adr_fastmcp_task_call_guardrails.md

Lines changed: 585 additions & 0 deletions
Large diffs are not rendered by default.

docs/norfab_changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44

55
1. Fixing nornir tests nfcli shell to pass verbose_on_fail to results processing, to make sure return results as table even when have failed tests.
66

7+
## FEATURES
8+
9+
1. Added FastMCP tool call guardrails to reject unsafe MCP tool calls before NorFab job dispatch. Guardrails can be defined by tasks or FastMCP inventory, support `regex`, `contains`, and `equals` checks, can disable built-in task guardrails with `tools.disable_builtin_guardrails`, and are visible through `show fastmcp tools`.
10+
711
## ENHANCEMENTS
812

913
1. Extended Nornir worker statistics with SID, NetBox, Containerlab inventory load statuses and managed `nornir_hosts` count.
1014
2. Enhanced NetBox worker REST filtering to route task-side `pynetbox.filter()` calls through adaptive `bulk_filter`, automatically chunking long list filters to avoid URI length errors while preserving query options such as `fields`, `limit`, and `ordering`.
1115
3. Improved result status coloring for nornir test task
1216
4. Nornir show hosts table added index column
17+
5. Enhanced Nornir `cli` task MCP metadata with default guardrails that block reboot, reload, restart, configuration-mode, shell-mode, OS/image/package operations, outbound `ssh`/`telnet`, commit, delete, clear, debug, reset, zeroize, and related state-changing commands when called through FastMCP.
18+
6. Enhanced Nornir `cfg` task MCP metadata with default guardrails that block reboot, reload, restart, `do ...` and `run ...` operational escapes, delete, erase, zeroize, and outbound `ssh`/`telnet` commands when called through FastMCP.
1319

1420
## CHANGES
1521

docs/workers/fastmcp/services_fastmcp_service_inventory.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,111 @@ tools:
217217
Task names can be inspected at runtime with the `show fastmcp tools` CLI
218218
command or the `get_tools` API call. Published prompts can be inspected
219219
with `show fastmcp prompts` or `get_prompts`.
220+
221+
---
222+
223+
## Tool Call Guardrails
224+
225+
FastMCP guardrails reject specific MCP tool calls by inspecting the call
226+
arguments before the NorFab job is dispatched. They are useful for allowing a
227+
flexible task such as `nornir.cli` while blocking unsafe command values.
228+
229+
Guardrails do not replace `tools.policy`. Policy decides whether a task is
230+
published and callable. Guardrails decide whether one specific call to an
231+
allowed task is acceptable.
232+
233+
Task authors can define default guardrails in task MCP metadata. FastMCP
234+
inventory can add deployment-specific guardrails under `tools.guardrails`.
235+
Built-in task guardrails are enabled by default.
236+
237+
```yaml
238+
tools:
239+
guardrails:
240+
- service: nornir
241+
task: cli
242+
description: Reject commit confirmed commands through MCP.
243+
field: commands
244+
type: contains
245+
match: commit confirmed
246+
message: "MCP guardrail rejected a commit confirmed command."
247+
```
248+
249+
`tools.guardrails` is a list of guardrail entries.
250+
251+
Set `tools.disable_builtin_guardrails: true` to ignore guardrails declared by
252+
tasks. Inventory-defined guardrails still apply:
253+
254+
```yaml
255+
tools:
256+
disable_builtin_guardrails: true
257+
guardrails:
258+
- service: nornir
259+
task: cli
260+
field: commands
261+
type: regex
262+
match: "(?i)^\\s*reload\\b.*"
263+
message: "MCP guardrail rejected a reload command."
264+
```
265+
266+
Inventory options:
267+
268+
| Key | Type | Description |
269+
|---|---|---|
270+
| `disable_builtin_guardrails` | boolean | Optional flag under `tools`; defaults to `false` |
271+
272+
Guardrail entry keys:
273+
274+
| Key | Type | Description |
275+
|---|---|---|
276+
| `service` | string | Exact NorFab service name, for example `nornir` |
277+
| `task` | string | Exact NorFab task name, for example `cli` |
278+
| `description` | string | Optional human-readable explanation for operators |
279+
| `field` | string | Top-level tool argument field to inspect |
280+
| `type` | `contains` / `equals` / `regex` | Match strategy |
281+
| `match` | string or list of strings | Text value or regex value to match |
282+
| `message` | string | Optional client-facing rejection message |
283+
284+
When `match` is a list, FastMCP rejects the call if any selected argument
285+
value matches any configured match value. `contains` and `equals` checks are
286+
always case-insensitive; regex checks use the flags declared in the regex
287+
value.
288+
289+
!!! warning
290+
Guardrails inspect only the inline MCP call arguments. They do not
291+
download NorFab URLs or inspect content resolved later by service workers,
292+
such as `nf://cli/commands.txt` files rendered by the Nornir worker at
293+
runtime.
294+
295+
Use NFCLI to inspect effective guardrails for a published task:
296+
297+
```bash
298+
show fastmcp tools service nornir name *cli*
299+
```
300+
301+
Example with several regex values:
302+
303+
```yaml
304+
tools:
305+
guardrails:
306+
- service: nornir
307+
task: cli
308+
description: Reject CLI commands that enter configuration mode.
309+
field: commands
310+
type: regex
311+
match:
312+
- "(?i)^\\s*configure\\s+terminal\\b.*"
313+
- "(?i)^\\s*configuration\\b.*"
314+
- "(?i)^\\s*conf\\s+t\\b.*"
315+
message: "MCP guardrail rejected a configuration mode command."
316+
```
317+
318+
The Nornir `cli` task includes default MCP guardrails that reject `reboot` and
319+
related reload or restart commands, configuration-mode commands such as
320+
`configure terminal`, `configuration`, and `conf t`, and destructive or
321+
state-changing commands such as `commit`, `delete`, `clear`, and `debug`.
322+
It also rejects shell-mode commands such as `bash`, `shell`, `start shell`,
323+
`request system shell`, and `guestshell`, OS/image/package operations, and
324+
outbound session commands such as `ssh` and `telnet`.
325+
The Nornir `cfg` task includes default MCP guardrails that reject operational
326+
command escapes in configuration input, such as `do ...` and `run ...`, plus
327+
reboot, reload, restart, delete, erase, zeroize, `ssh`, and `telnet` commands.

docs/workers/nornir/services_nornir_service_tasks_cfg.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ Nornir service `cfg` task designed to send configuration to devices using SSH an
2525

2626
The task returns per-host configuration results. When supported by the plugin and `add_details=True`, output can include `changed`, `diff`, `failed`, `exception`, `connection_retry`, and `task_retry` details.
2727

28+
## MCP Guardrails
29+
30+
When exposed through FastMCP, the `cfg` task includes default guardrails that
31+
reject configuration input attempting to execute operational commands from
32+
configuration mode, such as Cisco-style `do ...` and Junos-style `run ...`
33+
commands. They also reject reboot, reload, restart, delete, erase, zeroize,
34+
`ssh`, and `telnet` commands. These guardrails apply only to MCP tool calls.
35+
!!! warning
36+
Guardrails inspect inline `config` values only. If `config` points to a
37+
Filesharing path such as `nf://cfg/config.txt`, FastMCP checks the path
38+
string, not the downloaded or rendered file content.
39+
40+
Use NFCLI to inspect the currently published guardrails:
41+
42+
```bash
43+
show fastmcp tools service nornir name *cfg*
44+
```
45+
2846
## Examples
2947

3048
Example of sending configuration commands to devices.

docs/workers/nornir/services_nornir_service_tasks_cli.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ and NAPALM libraries to communicate with devices.
3131

3232
The task returns per-host command output keyed by command. With `add_details=True`, the result can include Nornir task metadata such as failure state, exception text, retry counters, and plugin-specific details.
3333

34+
## MCP Guardrails
35+
36+
When exposed through FastMCP, the `cli` task includes default guardrails that
37+
reject high-risk command values before a NorFab job is dispatched. The defaults
38+
block commands that reboot, reload, or restart devices; enter configuration
39+
mode; enter device shell modes such as `bash`, `shell`, or `guestshell`; or
40+
change OS images or packages; open outbound sessions such as `ssh` or
41+
`telnet`; or commit, delete, clear, debug, reset, or otherwise change device
42+
state.
43+
44+
These guardrails apply only to MCP tool calls. They do not change direct NorFab
45+
client, NFCLI, FastAPI, or worker behavior. FastMCP operators can add inventory
46+
guardrails or disable built-in guardrails with `tools.disable_builtin_guardrails`.
47+
!!! warning
48+
Guardrails inspect inline `commands` values only. If `commands` points to a
49+
Filesharing path such as `nf://cli/commands.txt`, FastMCP checks the path
50+
string, not the downloaded or rendered file content.
51+
52+
Use NFCLI to inspect the currently published guardrails:
53+
54+
```bash
55+
show fastmcp tools service nornir name *cli*
56+
```
57+
3458
## Examples
3559

3660
Below is an example of how to use the Nornir CLI task to retrieve command outputs from devices.

norfab/core/worker.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,11 @@ class Task:
357357
of MCP prompt definitions associated with the task. Task validates
358358
and normalizes these definitions before registration. FastMCP
359359
removes this key before constructing the MCP Tool object.
360+
The optional `guardrails` key is a NorFab extension containing a
361+
list of MCP task call guardrails. FastMCP validates these
362+
definitions during discovery, removes this key before constructing
363+
the MCP Tool object, and evaluates guardrails before dispatching
364+
tool calls to NorFab services.
360365
361366
Methods:
362367
__call__(function: Callable) -> Callable:

norfab/workers/fastmcp_worker/fastmcp_models.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
from typing import Any, Union
2-
3-
from pydantic import BaseModel, Field, StrictBool, StrictInt, StrictStr
1+
import re
2+
from typing import Any, Literal, Union
3+
4+
from pydantic import (
5+
BaseModel,
6+
ConfigDict,
7+
Field,
8+
StrictBool,
9+
StrictInt,
10+
StrictStr,
11+
model_validator,
12+
)
413

514
from norfab.models import Result
615

@@ -9,6 +18,30 @@
918
# --------------------------------------------------------------------------
1019

1120

21+
class TaskMCPGuardrail(BaseModel):
22+
model_config = ConfigDict(extra="forbid")
23+
24+
description: StrictStr = ""
25+
field: StrictStr = Field(..., min_length=1)
26+
type: Literal["contains", "equals", "regex"]
27+
match: Union[StrictStr, list[StrictStr]]
28+
message: StrictStr = ""
29+
30+
@model_validator(mode="after")
31+
def validate_regex_values(self) -> "TaskMCPGuardrail":
32+
if self.type != "regex":
33+
return self
34+
35+
values = self.match if isinstance(self.match, list) else [self.match]
36+
for value in values:
37+
try:
38+
re.compile(value)
39+
except re.error as exc:
40+
raise ValueError(f"Invalid guardrail regex match: {exc}") from exc
41+
42+
return self
43+
44+
1245
class GetVersionInput(BaseModel, use_enum_values=True, populate_by_name=True):
1346
pass
1447

0 commit comments

Comments
 (0)