Chio/Docs
LOGIN · JOIN

Requests & Action Classification

Trace a tool request to the action and the guards that inspect it, then test that your adapter’s shapes classify as intended.

Concept chio@5f63e6e

On this page

Catalog guards can apply file, network, shell, database, and memory policy to any mediated call, because each request is sorted into one shared vocabulary of actions. The tool name and the shape of its arguments decide which checks apply.

The integration submits the tool name, the arguments, and the authenticated request context. A guard using the shared extractor derives one action from them and checks the action types it governs; admission combines the configured checks before dispatch.

The first classifier that recognizes a request decides its action

extract_action_checked(tool_name, arguments) tries network, filesystem, shell, code execution, browser, database, memory write, memory read, and external API classification, in that order.

Action classificationChoose a request.
rread_file {"path": "notes.txt"}networkfilesystemshellcode executionbrowserdatabasememory writememory readexternal APIno matchFileAccess("notes.txt")rread_file {"path": "notes.txt"}networkfilesystemshellcode executionbrowserdatabasememory writememory readexternal APIno matchFileAccess

A file read stops at the filesystem classifier

read_file with a string path passes the network classifier and matches the filesystem classifier, which returns FileAccess("notes.txt"). Filesystem path rules apply to it. Filesystem.

The order extract_action_checked tries its classifiers, with the four requests from the classification tests. Computer-use metadata, request-text checks, and output checks use their own extraction and are left out.

The first recognized action or malformed-input error ends extraction. A request that no classifier recognizes returns McpTool with the original tool name and arguments.

Classifiers match implemented names and patterns. A custom tool stays a generic call even when one argument is called path: the filesystem classifier normalizes punctuation and recognizes selected patterns, and the other families use their own name lists. Check the exact request your integration sends.

An action describes the request; the grant authorizes it

The hosted ToolCallRequest carries the capability, tool name, arguments, and applicable request metadata. The kernel passes each guard a GuardContext with the verified scope, the agent and server identities, and the runtime context. A guard can inspect those fields directly or derive a ToolAction from the request.

The action is a categorized view of what was submitted. It records nothing about what the tool later did, and it carries no authority of its own. Scope matching establishes the applicable grant, and the GuardContext reference defines the fields a guard receives.

Common request shapes map to one action and one control

Representative tools, the action extraction returns for each, and the control that inspects that action.

Tool and argumentsExtracted actionRelevant control
read_file with a string pathFileAccessFilesystem path rules
write_file with string path and contentFileWritePath rules and configured secret scanning
fetch with {"url":"https://example.com"}NetworkEgress: example.com, 443Egress and internal-network rules
shell_exec with a string commandShellCommandShell command rules
query with a string queryDatabaseQueryConfigured SQL checks
remember with string store and keyMemoryWriteMemory governance
publish_report with a path argumentMcpToolGeneric tool access and application checks

Each row describes extraction. Whether the kernel admits the call depends on the installed guards, their configuration, the authority presented, and runtime state. Computer-use guards also recognize action metadata directly, and request-text and output checks have their own extraction rules.

Inspect the complete ToolAction type
crates/guards/chio-guards/src/action.rs15-45rust
pub enum ToolAction {
    /// File system read (path).
    FileAccess(String),
    /// File system write (path, content bytes).
    FileWrite(String, Vec<u8>),
    /// Network egress (host, port).
    NetworkEgress(String, u16),
    /// Shell command execution (command line).
    ShellCommand(String),
    /// MCP tool invocation (tool_name, args).
    McpTool(String, Value),
    /// Patch application (file, diff).
    Patch(String, String),
    /// Code execution via an interpreter (language, code snippet).
    CodeExecution { language: String, code: String },
    /// Browser automation action (verb, optional target URL).
    BrowserAction {
        verb: String,
        target: Option<String>,
    },
    /// Database query (database/engine identifier, raw query text).
    DatabaseQuery { database: String, query: String },
    /// External API call (service name, endpoint/path).
    ExternalApiCall { service: String, endpoint: String },
    /// Agent memory write (store/collection id, key).
    MemoryWrite { store: String, key: String },
    /// Agent memory read (store/collection id, optional key).
    MemoryRead { store: String, key: Option<String> },
    /// Unknown / not categorized -- guards that don't match should allow.
    Unknown,
}

A recognized tool with a malformed field is refused

A recognized tool with a missing required field, or a field of the wrong type, produces MalformedAction. read_file with a numeric path fails checked extraction, and a guard using this boundary refuses the request.

Field aliases are tried in order. Filesystem paths use path, file, file_path, then filename. When the first alias present has the wrong type, extraction fails there instead of skipping to a later valid alias. Preserve these cases in adapter tests.

The convenience helper extract_action converts an extraction error into Unknown, so guards call the checked helper. An unfamiliar tool becomes McpTool, which is a different case from a recognized tool whose required fields are malformed.

The generic tool guard covers what no classifier claims

McpToolGuard uses checked extraction and applies its name lists and argument-size limit to the McpTool branch. Every other successfully classified action passes this guard with Allow, so its name lists sit beside capability scope matching and the specialized guards.

A well-formed shell_exec request shows the consequence. Its name appears in the generic tool guard’s default block list, yet the request classifies as ShellCommand and takes the shell branch. To test whether execution is refused, inspect the configured shell guard and the capability grant.

A domain guard’s Allow can mean its policy passed or the action was outside its domain, because the verdict has no separate value for a guard that does not apply. A custom guard that needs one rule across action families can inspect the raw request. The trait reference and the guard catalog cover those choices.

Your adapter’s request shapes become a test contract

In a Rust guard project that depends on chio-guards and serde_json, add these assertions to the test module, with the representative names replaced by the exact names your adapter emits.

classification tests · in your guard craterust
#[test]
fn request_shapes_select_the_expected_actions() {
    use chio_guards::{extract_action_checked, ToolAction};
    use serde_json::json;

    let read = extract_action_checked("read_file", &json!({"path": "notes.txt"}));
    assert!(matches!(read, Ok(ToolAction::FileAccess(path)) if path == "notes.txt"));

    let shell = extract_action_checked("shell_exec", &json!({"command": "pwd"}));
    assert!(matches!(shell, Ok(ToolAction::ShellCommand(command)) if command == "pwd"));

    let custom = extract_action_checked("publish_report", &json!({"path": "notes.txt"}));
    assert!(matches!(custom, Ok(ToolAction::McpTool(name, _)) if name == "publish_report"));

    let malformed = extract_action_checked("read_file", &json!({"path": 42}));
    assert_eq!(malformed.unwrap_err().field, "path");

    let conflicting_aliases = extract_action_checked(
        "read_file", &json!({"path": 42, "file": "notes.txt"}),
    );
    assert!(conflicting_aliases.is_err());
}

These assertions cover classification. Next, evaluate the intended guard under a permissive and a restrictive configuration. Then submit both cases through the application and inspect the effect: a denied write leaves the document unchanged, and the signed receipt carries the decision and any guard evidence. An absent evidence row alone leaves open whether a guard ran.

  • Align the arguments. The adapter’s request fields and the tool’s executed arguments name the same target; a check of one path authorizes that path only.
  • Confirm the installation. Check both the registered guard and its enabled configuration. A catalog entry describes a guard; registration installs it.
  • Map custom tools deliberately. Define a mapping or write a guard for the tool’s request schema, instead of renaming the tool to trigger a heuristic.
  • Route the effect through admission. Direct credentials or an unwrapped endpoint create a separate execution path.

The shared classifier lives in action/extractor.rs; the public types and their tests live in action.rs. Each guard owns its use of those helpers, and the complete guard test project supplies the surrounding build and application checks.

Next: choose the control for your action