> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.method.security/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.method.security/_mcp/server.

# Tool authoring reference

> The full definition structure for integrating custom Tools into Method, including fields, parameters, compilers, processors, validation, and worked examples.

The reference for authoring a custom [Tool](/platform/core-platform/tools) definition: the fields a Tool needs, how parameters and targets work, the Compiler and Processor contracts, how to validate and register, and a set of worked examples.

Follow [Create and publish a Tool](/guides/tools/create-and-publish-a-tool) for the product workflow. This page describes the definition fields and implementation interfaces.

> **Note**
>
> Authoring capabilities and Object schemas depend on your Method instance. Use its Object schemas and connected authoring guidance to verify the available contracts. Contact your Method representative if Tool authoring is not enabled.

> **Note**
>
> Method open-sources its Tools on [GitHub](https://github.com/method-security).
> You can see exactly how Method's own Tools are built, and if you build
> something useful, contribute it back.

---

## The Tool definition

The Tool definition describes the metadata, inputs, execution, and output processing for the capability. In the code editor, `tool.json` holds the definition and can reference separate Compiler and Processor files. The authoring tools resolve those files when submitting the definition to Method.

| Field              | Required | What to put there                                                                                                                 |
| ------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `name`             | Yes      | Lowercase alphanumeric name, such as `hostinventory`. Method strips any non `[a-z0-9]` characters.                                |
| `displayName`      | Yes      | Human-readable name.                                                                                                              |
| `version`          | Yes      | Semantic version, usually `0.0.1` for a first Tool.                                                                               |
| `family`           | Yes      | MITRE tactic family, such as `DISCOVERY` or `CREDENTIAL_ACCESS`.                                                                  |
| `intent`           | Yes      | Purpose, results, effects, required access, cleanup, and failure behavior. This helps operators and Agents assess the capability. |
| `riskLevel`        | Yes      | `LOW`, `MEDIUM`, or `HIGH`. Use `HIGH` for changing systems, authenticating, exploiting, exfiltrating, or persistence.            |
| `techniques`       | Yes      | List of MITRE ATT\&CK technique IDs. An empty list is allowed.                                                                    |
| `parameters`       | Yes      | Direct and Ontology input definitions. An empty list is allowed.                                                                  |
| `requirements`     | Yes      | Required parameter groups. Use `{"oneOfParameters": []}` when nothing is required.                                                |
| `compiler`         | Yes      | Exactly one compiler: `scriptTool`, `cliTool`, or `courierTool`.                                                                  |
| `processor`        | Yes      | A `customProcessor` with one inline restricted Python Processor class.                                                            |
| `generatesTypes`   | Yes      | Every Ontology Object Type the Processor creates.                                                                                 |
| `successCriteria`  | Yes      | An Object filter that should match an Object proving the run succeeded.                                                           |
| `examples`         | Yes      | User-facing examples. An empty list is allowed, but examples improve recommendation.                                              |
| `timeoutInSeconds` | Optional | Execution timeout. Use a realistic value, such as `300`.                                                                          |

## Families

A Tool belongs to exactly one MITRE tactic family:

`RECONNAISSANCE`, `RESOURCE_DEVELOPMENT`, `INITIAL_ACCESS`, `EXECUTION`, `PERSISTENCE`, `PRIVILEGE_ESCALATION`, `DEFENSE_EVASION`, `CREDENTIAL_ACCESS`, `DISCOVERY`, `LATERAL_MOVEMENT`, `COLLECTION`, `COMMAND_AND_CONTROL`, `EXFILTRATION`, `IMPACT`.

## Authoring workflow

The Tool's output determines its generated Object Types and success criteria. Its inputs determine the Compiler's parameters and targets. The Processor connects the two by interpreting execution output as Objects and relationships.

For the build sequence, follow [Create and publish a Tool](/guides/tools/create-and-publish-a-tool).

---

## Parameters

A Tool takes two kinds of input.

| Category | Use when                                                                   | Examples                                            |
| -------- | -------------------------------------------------------------------------- | --------------------------------------------------- |
| Direct   | The user types a value or chooses a simple setting                         | `domain`, `timeout`, `verbose`, `ports`, `username` |
| Ontology | The user selects existing Method Objects, which Method resolves to strings | `ipAddresses`, `fqdns`, `urls`, `smbApplications`   |

### Direct parameters

| Direct type    | Python value in `input.tool_parameters` | Good use                        |
| -------------- | --------------------------------------- | ------------------------------- |
| `STRING`       | `str`                                   | Domain, username, path, mode    |
| `STRING_ARRAY` | `list[str]`                             | Resolver list, wordlist entries |
| `BOOLEAN`      | `bool`                                  | `verbose`, `includeDisabled`    |
| `INTEGER`      | `int`                                   | Timeout, retry count, port      |
| `FLOAT`        | `float`                                 | Delay, threshold                |

### Ontology parameters

An Ontology parameter points at an Object Type by its identifier. Common ones:

| Object                   | Identifier                                       |
| ------------------------ | ------------------------------------------------ |
| IP address               | `ri.ontology..objecttype.ipaddress`              |
| FQDN                     | `ri.ontology..objecttype.fqdn`                   |
| URL                      | `ri.ontology..objecttype.url`                    |
| Port                     | `ri.ontology..objecttype.port`                   |
| Host                     | `ri.ontology..objecttype.host`                   |
| Network application      | `ri.ontology..objecttype.networkapplication`     |
| SMB application          | `ri.ontology..objecttype.smbapplication`         |
| SMTP application         | `ri.ontology..objecttype.smtpapplication`        |
| DNS record               | `ri.ontology..objecttype.dnsrecord`              |
| Credential               | `ri.ontology..objecttype.credential`             |
| Local user account       | `ri.ontology..objecttype.localuseraccount`       |
| Active Directory account | `ri.ontology..objecttype.activedirectoryaccount` |
| CVE                      | `ri.ontology..objecttype.cve`                    |
| Web application          | `ri.ontology..objecttype.webapplication`         |
| Certificate              | `ri.ontology..objecttype.certificate`            |

### Requirements

The definition's `requirements` field describes which parameter groups the Compiler requires.

Keep requirements aligned with the parameters and targets used by the Compiler. Validate representative input combinations, including missing required values, before publishing.

---

## Compilers

A Compiler deterministically turns typed Tool inputs and selected Ontology Objects into the execution plan a Jackal receives. It is not a place for an AI to improvise commands. Keep parameters minimal, validate every value that changes behavior, and reject unsupported combinations with a clear `CompileException` before the Tool runs.

A Tool uses exactly one compiler type.

| Compiler                  | Use when                                          | Author writes                                      |
| ------------------------- | ------------------------------------------------- | -------------------------------------------------- |
| `scriptTool`              | You can express the Tool as shell commands        | OS-specific command strings, optional substitution |
| `cliTool` with directUrls | You have a downloadable binary per OS and arch    | Fixed args, download URLs, `populateParameters`    |
| `cliTool` with installed  | The executable already exists on the Jackal Host  | Installed paths and `populateParameters`           |
| `cliTool` with Artifact   | The binary is stored as a Method Artifact family  | Artifact family and version, `populateParameters`  |
| `courierTool`             | The Tool needs direct workflow and action control | A Courier workflow in `populateParameters`         |

### Script Tool

Script Tools are the simplest shape and need no packaged binary, only named shell commands. They run on a deployed [Jackal](/platform/architecture/jackal-c2).

| Field                    | Meaning                                                         |
| ------------------------ | --------------------------------------------------------------- |
| `supportedArchitectures` | `AMD_64`, `ARM_64`.                                             |
| `targets`                | Empty for no Ontology target. Non-empty for one run per target. |
| `shellCompilers`         | List of OS-specific command sets.                               |
| Command name             | Stable Signal key. The Processor reads output under this name.  |
| Command string           | The shell command to run.                                       |
| `populateParameters`     | Optional. Required for `<<placeholder>>` substitution.          |

OS targeting supports Linux (any distribution or a specific one such as Ubuntu), macOS, and Windows (desktop or Server).

Prefer generic Linux with `distribution: null` unless the Tool genuinely depends on a narrower distribution. Explicitly list every supported architecture. Most portable Linux Tools support both `AMD_64` and `ARM_64`.

#### Static script tools

A static Script Tool runs fixed commands with no parameter substitution. Each command has a name and a shell string. The name is how the Processor identifies that command's stdout output.

#### Parameterized script tools

To use direct parameters in shell commands, add `populateParameters` and reference values as `<<name>>` placeholders.

| Piece                  | Rule                                                                                                                               |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Placeholder syntax     | Use literal `<<name>>` in commands.                                                                                                |
| Source of values       | `populateParameters` defines one `MethodToolScriptCompiler` subclass.                                                              |
| Value type             | Each substitution must be a plain string.                                                                                          |
| Replacement behavior   | Raw text replacement. Validate values and quote them for the target shell; surrounding quotes alone do not escape untrusted input. |
| Unresolved placeholder | Validation fails.                                                                                                                  |

```python
class ResolveScriptParameters(MethodToolScriptCompiler):
    def compile(self, input: MethodToolCompileInput, replacements: ScriptReplacements) -> None:
        domain = input.get("domain")
        timeout = input.get("timeout") or 5
        if not domain or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9.-]*", str(domain)):
            raise CompileException("domain must contain only hostname characters")
        if not isinstance(timeout, int) or isinstance(timeout, bool) or timeout < 1 or timeout > 60:
            raise CompileException("timeout must be an integer from 1 to 60")
        replacements.set("domain", str(domain))
        replacements.set("timeout", str(timeout))
```

```bash
timeout '<<timeout>>' getent ahosts '<<domain>>' || true
```

#### Target-aware script tools

Targets are selected from configured Ontology Parameters. When a target is set, the Tool runs once per resolved Object. Use the target access pattern for the compiler branch you selected, and validate target assumptions before adding them to a command. Use the Compiler validator with representative target Objects to inspect the final command for every supported platform and architecture.

### CLI Tool

Use `cliTool` when execution is a binary plus arguments. Method resolves the binary, then appends fixed `commandArgs` and the dynamic arguments from `populateParameters`.

| Field                    | Meaning                                                                    |
| ------------------------ | -------------------------------------------------------------------------- |
| `supportedPlatforms`     | OS list, same shape as Script Tools.                                       |
| `supportedArchitectures` | `AMD_64`, `ARM_64`.                                                        |
| `commandArgs`            | Fixed tokens after the executable. Do not include the executable path.     |
| `targets`                | Ontology target definitions. Empty means one untargeted planned execution. |
| `populateParameters`     | Restricted Python deriving from `MethodToolCliCompiler`.                   |
| `toolLocation`           | One of `directUrls`, `installed`, or `artifact`.                           |

```python
class BuildCliArguments(MethodToolCliCompiler):
    def compile(self, input: MethodToolCompileInput, arguments: CliArguments) -> None:
        target = input.get("targetHost")
        timeout = input.get("timeout") or 30
        verbose = input.get("verbose") or False
        if not target:
            raise CompileException("targetHost is required")
        arguments.add_argument("--target", str(target))
        arguments.add_argument("--timeout", str(timeout))
        if verbose:
            arguments.add_argument("--verbose", "true")
```

#### Tool locations

| Location    | Use when                                           |
| ----------- | -------------------------------------------------- |
| Direct URLs | You have a downloadable binary hosted at a URL.    |
| Installed   | The binary is already present on the Jackal host.  |
| Artifact    | The binary is stored in Method's Artifact service. |

### Courier Tool

Use `courierTool` when the Tool needs direct control over a Courier workflow, such as composed actions, explicit outcomes, file transfer, cleanup, or a Jackal-specific action. Prefer Script or CLI when their higher-level primitives express the capability cleanly. Courier is more flexible, but it exposes more execution details and failure modes.

Define one `MethodToolCourierCompiler` subclass in `populateParameters`. Its `compile` method adds one or more workflows to the injected `CourierWorkflows` collection. Do not add imports: Method injects `Workflow`, `CourierWorkflows`, `MethodToolCompileInput`, and `CompileException` into the sandbox.

```python
class GatherFileCompiler(MethodToolCourierCompiler):
    def compile(self, input: MethodToolCompileInput, workflows: CourierWorkflows) -> None:
        file_identifier = input.get("fileIdentifier")
        if not file_identifier:
            raise CompileException("fileIdentifier is required")
        workflow = Workflow()
        steps = [
            workflow.actions.download_file(
                file_identifier=file_identifier,
                target_path="/tmp/tool-input",
                signal="downloaded",
            ),
            workflow.actions.exfil_file(
                path="/tmp/tool-input",
                signal="exfiltrated",
                base64_contents="file_base64",
            ),
        ]
        workflows.add_workflow(workflow.build(steps=steps))
```

Each output that a Processor needs must be a named Signal. First-class actions declare the Signal when you pass `signal="name"`. For custom actions, explicitly declare the Signal in the enclosing step's outcomes. Otherwise the action can run successfully but the Processor cannot receive its output.

```python
workflow = Workflow()
action = workflow.actions.custom(action_type_id=9001, action_contents=b"tool-input")
outcomes = workflow.outcomes.create(generates_signals=["custom_output"])
step = workflow.steps.create(action=action, outcomes=outcomes)
workflows.add_workflow(workflow.build(steps=[step]))
```

---

## Processors

A Processor turns raw Signals into durable, reusable Ontology knowledge. It is not an execution log. Model what the Tool discovered, such as a host, service, account, credential, vulnerability, configuration, or finding. Keep commands, wrappers, timestamps, exit codes, and raw diagnostics in Signals, where Method retains them for auditability.

Define exactly one `MethodToolProcessor` subclass and return generated Ontology Objects in `ProcessResult`.

### Working in restricted Python

Inline Processor code runs in a restricted sandbox. Repository source files can include SDK imports supplied by starter templates; follow the authoring environment's source-loading and validation guidance for those files. For code passed directly to the restricted runtime:

* Do not use `import`. Method injects `json`, `re`, `method_ontology_sdk`, generated Ontology constructors, and the SDK base and result classes.
* Define exactly one Processor subclass.
* Do not access private or dunder attributes such as `__dict__`.
* Do not perform file, network, subprocess, eval, dynamic import, or package installation.
* Do not define helpers whose names begin with `_`.
* Every Object Type your Processor creates must appear in `generatesTypes`.
* Use the connected Method development tools to look up Object schemas, constructor fields, and link helper names.

```python
class JsonReportProcessor(MethodToolProcessor):
    def process(self, output: MethodToolOutput) -> ProcessResult:
        raw = output.get_raw_output()
        if not raw:
            return ProcessResult(success=False, error_message="missing report output")
        try:
            data = json.loads(raw)
        except ValueError as exc:
            return ProcessResult(success=False, error_message=f"invalid report JSON: {exc}")
        addresses = [
            IpAddress(ip_address=item["ip"])
            for item in data.get("addresses") or []
            if item.get("ip")
        ]
        return ProcessResult(success=bool(addresses), new_ontology=addresses)
```

When the Tool emits exactly one output, call `output.get_raw_output()`. For multiple named Signals or workflow steps, call `output.get_raw_output(step="signal_name")`. Use generated constructors and link helpers to create Objects and their relationships:

```python
ip = IpAddress(ip_address="192.0.2.10")
host = Host(hostname="WEB01").with_ip_address(ip)
return ProcessResult(success=True, new_ontology=[ip, host])
```

Handle output according to the Tool's semantics. Empty output can be a valid result for an enumeration Tool. Malformed output should fail or warn intentionally. Never silently create placeholder Objects from an incomplete report.

---

## Success criteria

`successCriteria` is an Object search filter that should match the Object that best proves the Tool worked.

| Tool kind           | Good success Object                                          |
| ------------------- | ------------------------------------------------------------ |
| DNS lookup          | `ipaddress` or `dnsrecord`                                   |
| Port discovery      | `port`                                                       |
| Service enumeration | `smtpapplication`, `smbapplication`, or `networkapplication` |
| Host inventory      | `host`                                                       |
| Credential capture  | `credential`                                                 |
| Web discovery       | `url`, `webapplication`, or `webendpoint`                    |

Keep the Processor's `success=True` aligned with the success criteria you set. A run that reports success but creates no matching Object makes the UI and recommendation logic inconsistent.

---

## Validation and registration

The authoring tools validate three parts of a Tool:

| Validation | What it checks                                                                  | What to inspect                                               |
| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Definition | Metadata, parameters, target types, tags, generated types, and success criteria | A consistent definition of the Tool's behavior                |
| Compiler   | The execution plan for representative inputs and mock targets                   | Commands, arguments, platform selection, and workflow actions |
| Processor  | The interpretation of sample Signals or payload                                 | Expected Objects, relationships, and failure behavior         |

Changes to any of these parts can affect the others. The guides describe the full validation sequence to repeat before publication.

Published versions are immutable. Saving a draft, editing source, committing, pushing, and validating are separate from publishing a runnable version. The Method extension's Publish action saves and commits pending changes, pushes the release, and publishes the version. See [Create and publish a Tool](/guides/tools/create-and-publish-a-tool) for the procedure.

Validation checks a definition, a compiled plan, and sample output. It does not execute the Tool in a target environment. Test the published version separately to verify its real execution and resulting Objects.

---

## Examples

### Script Tool, no target: host inventory

The Jackal host itself is the target, so the Tool needs no Ontology input. A good first Tool.

**OS:** Linux, any distribution

**Commands:**

| Step | Name        | Command                                            |
| ---- | ----------- | -------------------------------------------------- |
| 1    | `hostname`  | `hostname`                                         |
| 2    | `addresses` | `ip -o -4 addr show \| awk '{print $4}' \|\| true` |

```python
class HostInventoryProcessor(MethodToolProcessor):
    def process(self, output: MethodToolOutput) -> ProcessResult:
        host_lines = (output.get_raw_output(step="hostname") or "").strip().splitlines()
        host_name = host_lines[0].strip() if host_lines else None
        ip_objects = [
            IpAddress(ip_address=ip)
            for ip in re.findall(
                r"(?:\d{1,3}\.){3}\d{1,3}", output.get_raw_output(step="addresses") or ""
            )
        ]
        if not host_name and not ip_objects:
            return ProcessResult(success=False)
        if not host_name:
            return ProcessResult(success=True, new_ontology=ip_objects)
        host = Host(hostname=host_name)
        for ip in ip_objects:
            host = host.with_ip_address(ip)
        return ProcessResult(success=True, new_ontology=[*ip_objects, host])
```

### Script Tool, IP targets: reachability check

The user selects IP Address Objects, and the script runs once per target.

**OS:** Linux, any distribution
**Target:** IP Address (one run per selected Object)

**Commands:**

| Step | Name   | Command                                                                                                                                        |
| ---- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | `ping` | `timeout '<<timeout>>' ping -c 1 '<<target>>' >/dev/null 2>&1 && echo 'REACHABLE target=<<target>>' \|\| echo 'UNREACHABLE target=<<target>>'` |

```python
class ReachabilityProcessor(MethodToolProcessor):
    def process(self, output: MethodToolOutput) -> ProcessResult:
        raw = output.get_raw_output(step="ping") or ""
        addresses = [
            IpAddress(ip_address=match)
            for match in re.findall(r"(?m)^REACHABLE target=([^\s]+)$", raw)
        ]
        return ProcessResult(success=bool(addresses), new_ontology=addresses)
```

### CLI Tool, direct URLs: JSON report

The binary downloads per OS and architecture and emits one JSON report.

**Location:** Direct URLs (Linux x86_64 and arm64)
**Fixed arguments:** `discover dns forward`
**Target:** FQDN (one run per selected Object)

```python
class DnsForwardProcessor(MethodToolProcessor):
    def process(self, output: MethodToolOutput) -> ProcessResult:
        data = json.loads(output.get_raw_output() or "{}")
        addresses = []
        for lookup in (data.get("result") or {}).get("lookups") or []:
            ip = lookup.get("ipAddress")
            if not ip:
                continue
            addresses.append(IpAddress(ip_address=ip))
        return ProcessResult(success=bool(addresses), new_ontology=addresses)
```

---

## Common pitfalls

| Symptom                                        | Likely cause                                     | Fix                                                                                 |
| ---------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------- |
| Validation says a generated type is missing    | `generatesTypes` omits an Object Type            | Add every Object Type the Processor creates.                                        |
| Compiler says `mockTarget` is required         | Tool has targets but validation omitted one      | Supply a realistic target string.                                                   |
| Command has unresolved placeholders            | `env_vars` key does not match `<<placeholder>>`  | Match names exactly and return plain strings.                                       |
| Processor returns success but UI shows nothing | The success-criteria Object was not created      | Align `successCriteria` with the Processor output.                                  |
| Tool does not appear for selected Objects      | Target parameter type cannot resolve to a string | Use an Object Type that resolves, such as IP Address or FQDN.                       |
| Python compiles locally but fails validation   | Sandbox blocks imports or private-style helpers  | Remove imports, file IO, subprocesses, introspection, and names beginning with `_`. |

## Final review checklist

* Tool name is lowercase alphanumeric and versioned.
* Risk level accurately reflects expected operational impact.
* Required parameters match the Compiler's assumptions.
* Every Ontology target parameter appears in Compiler targets.
* Script substitution values are validated, quoted for the target shell, and fully substituted.
* CLI `commandArgs` do not include the binary path.
* Processor defines exactly one `MethodToolProcessor` subclass.
* `generatesTypes` includes every Object Type the Processor creates.
* Success criteria matches a created Object Type.
* A valid sample output and an empty or negative output were both tested.
* Compiler validation passed for every OS and architecture you support.

---

> **Note**
>
> For the product walkthrough, see [Create and publish a Tool](/guides/tools/create-and-publish-a-tool).
> For how Tools fit into the platform, see [Tools](/platform/core-platform/tools).