METHODS.toml Manifest Format
The METHODS.toml file is the package manifest — the identity card and dependency declaration for an MTHDS package. It MUST be named exactly METHODS.toml and MUST be located at the root of the package directory.
File Encoding and Syntax
METHODS.toml MUST be a valid TOML document encoded in UTF-8.
Top-Level Sections
A METHODS.toml file contains up to three top-level sections:
| Section | Required | Description |
|---|---|---|
[package] |
Yes | Package identity and metadata. |
[dependencies] |
No | Dependencies on other MTHDS packages. |
[exports] |
No | Visibility declarations for pipes. |
The [package] Section
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The name of the method. MUST be snake_case (matching [a-z][a-z0-9_]*), 2-25 characters. See Name. |
address |
string | Yes | Globally unique package identifier. MUST follow the hostname/path pattern. |
display_name |
string | No | Human-friendly display label. When provided, MUST NOT be empty or whitespace-only, MUST NOT exceed 128 characters, and MUST NOT contain Unicode control characters (category Cc). Leading and trailing whitespace is stripped. |
version |
string | Yes | Package version. MUST be valid semantic versioning (MAJOR.MINOR.PATCH, with optional pre-release and build metadata). |
description |
string | Yes | Human-readable summary of the package's purpose. MUST NOT be empty. |
authors |
array of strings | No | List of author identifiers (e.g., "Name <email>"). Default: empty list. |
license |
string | No | SPDX license identifier (e.g., "MIT", "Apache-2.0"). |
mthds_version |
string | No | MTHDS standard version constraint. If set, MUST be a valid version constraint. |
main_pipe |
string | No | The package's entry-point pipe code. MUST be snake_case (matching [a-z][a-z0-9_]*). Auto-exported by virtue of being main_pipe. See Main Pipe. |
exclude |
array of strings | No | Patterns, in .gitignore syntax, naming files to keep out of the package's method tree. See Exclude. |
Address Format
The package address is the globally unique identifier for the package. It doubles as the fetch location for VCS-based distribution.
For a package at its repository root, the manifest address alone is that identity. In a library repository — a repository hosting several packages — the packages share the repository's address in their manifests, and each package's full, globally unique address is address + "/" + name (see Name and Distribution: Locating a Package Inside a Clone).
Pattern: ^[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+/[a-zA-Z0-9._/-]+$
In plain language: the address MUST start with a hostname (containing at least one dot), followed by a /, followed by one or more path segments.
Examples of valid addresses:
github.com/acme/legal-tools
github.com/mthds/document-processing
gitlab.com/company/internal-methods
Examples of invalid addresses:
legal-tools # No hostname
acme/legal-tools # No dot in hostname
Name
The name field is the name of the method. It is the primary identifier for the method — it appears in CLI output, registry listings, and documentation.
Constraints:
- MUST be
snake_case, matching the pattern[a-z][a-z0-9_]*. - MUST be between 2 and 25 characters.
- MUST NOT be empty.
The manifest is the identity: the directory (or repository) that contains a package may carry any name, and a compliant tool MUST NOT require the directory name to match name. Where a fetched or cached copy of a package lives on disk is a tool concern, not part of the standard.
The name also participates in locating a package inside a repository that hosts several packages: a package's full address is its address when the package sits at the repository root, or address + "/" + name when it is one of several packages in a library repository. See Distribution: Locating a Package Inside a Clone.
Example:
[package]
name = "nda_analyzer"
address = "github.com/acme/legal-tools"
Display Name
The optional display_name field provides a human-friendly label for the package. It appears in CLI output, registry listings, and error messages. It is cosmetic only — the address remains the sole canonical identifier.
Constraints:
- MUST NOT be empty or whitespace-only when provided.
- MUST NOT exceed 128 characters (after leading/trailing whitespace is stripped).
- MUST NOT contain Unicode control characters (Unicode general category
Cc). - Emojis and other Unicode characters are allowed.
- Leading and trailing whitespace is stripped by a compliant implementation.
Example:
[package]
name = "nda_analyzer"
address = "github.com/acme/legal-tools"
display_name = "Nda Analyzer"
Version Format
The version field MUST conform to Semantic Versioning 2.0.0:
MAJOR.MINOR.PATCH[-pre-release][+build-metadata]
Examples: 1.0.0, 0.3.0, 2.1.3-beta.1, 1.0.0-rc.1+build.42
mthds_version Constraints
The mthds_version field, if present, declares which versions of the MTHDS standard this package is compatible with. It uses version constraint syntax (see Version Constraint Syntax).
The current MTHDS standard version is 8.0.0. See Versioning for what moves it.
Main Pipe
The optional main_pipe field designates the package's primary entry point — the pipe that runs when a user invokes the package by name or address:
mthds run method nda_analyzer
mthds run method github.com/acme/legal-tools
Constraints:
- The value MUST be a valid
snake_casepipe code (matching[a-z][a-z0-9_]*). - The referenced pipe is auto-exported: it is part of the package's public surface by virtue of being
main_pipe, whether or not it also appears in[exports]. There is no requirement to list it there. See The[exports]Section. - When
main_pipeis not set, the package has no default entry point. It can still be consumed as a library by importing specific pipes.
Example:
[package]
name = "nda_analyzer"
address = "github.com/acme/legal-tools"
version = "0.3.0"
main_pipe = "analyze_nda"
Exclude
The optional exclude field lists patterns naming files to keep out of the package's method tree without keeping them out of git, such as sample data an author keeps in the repository but that no run needs:
[package]
name = "invoice_letters"
address = "github.com/acme/invoice-letters"
version = "1.0.0"
exclude = ["samples/", "*.psd"]
Constraints:
- Each entry is one pattern in the syntax of a
.gitignorefile, as gitignore documents it, and MUST NOT hold a line break. An entry starting with#is a comment, as it would be in a.gitignorefile, and\#escapes it. - The patterns are read relative to the package's root, as git reads
core.excludesFile, below every.gitignorefile of the tree in precedence. The Membership Rule states how they combine with the tree's other exclusions. - Each pattern is normalized to NFC before it is matched, as the membership rule normalizes every pattern, so a pattern written in decomposed form matches the same names as one written composed.
The [dependencies] Section
Implementation status
Dependency declaration is committed design. This section is the contract implementations are brought into conformance with — the same forward-contract convention the Library Crate Format uses for its unrealized sections. Conformance is asserted against this document as each piece lands.
Each entry in [dependencies] declares a dependency on another MTHDS package. The key is the alias — a snake_case identifier used in cross-package references (-> syntax).
[dependencies]
docproc = { address = "github.com/mthds/document-processing", version = "^1.0.0" }
scoring_lib = { address = "github.com/mthds/scoring-lib", version = "^0.5.0" }
Dependency Fields
| Field | Type | Required | Description |
|---|---|---|---|
address |
string | Yes | The dependency's package address. MUST follow the hostname/path pattern. |
version |
string | Yes | Version constraint for the dependency (see Version Constraint Syntax). |
path |
string | No | Local filesystem path to the dependency, resolved relative to the manifest directory. For development-time workflows. |
Alias Rules
- The alias (the TOML key) MUST be
snake_case, matching[a-z][a-z0-9_]*. - All aliases within a single
[dependencies]section MUST be unique. - The alias is used in cross-package references:
alias->domain.name.
The path Field
When path is set, the dependency is resolved from the local filesystem instead of being fetched via VCS. This supports development-time workflows where packages are co-located on disk, similar to Cargo's path dependencies or Go's replace directives.
- The path is resolved relative to the directory containing
METHODS.toml. - Local path dependencies are NOT resolved transitively — only the root package's local paths are honored.
- Local path dependencies are excluded from the lock file.
Example:
[dependencies]
scoring = { address = "github.com/mthds/scoring-lib", version = "^0.5.0", path = "../scoring-lib" }
Version Constraint Syntax
Version constraints specify which versions of a dependency are acceptable.
| Form | Syntax | Example | Meaning |
|---|---|---|---|
| Exact | MAJOR.MINOR.PATCH |
1.0.0 |
Exactly this version. |
| Caret | ^MAJOR.MINOR.PATCH |
^1.0.0 |
Compatible release (same major version). |
| Tilde | ~MAJOR.MINOR.PATCH |
~1.0.0 |
Approximately compatible (same major.minor). |
| Greater-or-equal | >=MAJOR.MINOR.PATCH |
>=1.0.0 |
This version or newer. |
| Less-than | <MAJOR.MINOR.PATCH |
<2.0.0 |
Older than this version. |
| Greater | >MAJOR.MINOR.PATCH |
>1.0.0 |
Newer than this version. |
| Less-or-equal | <=MAJOR.MINOR.PATCH |
<=2.0.0 |
This version or older. |
| Equal | ==MAJOR.MINOR.PATCH |
==1.0.0 |
Exactly this version. |
| Not-equal | !=MAJOR.MINOR.PATCH |
!=1.0.0 |
Any version except this one. |
| Compound | constraint , constraint |
>=1.0.0, <2.0.0 |
Both constraints must be satisfied. |
| Wildcard | *, MAJOR.*, MAJOR.MINOR.* |
1.* |
Any version matching the prefix. |
Partial versions are allowed: 1.0 is equivalent to 1.0.*.
Constraints are read as floors. Version resolution follows Minimum Version Selection: the resolver selects the lowest version that satisfies every constraint declared for a package across the dependency graph. A declared constraint therefore states the minimum a dependent is known to work with — its floor — and the resolved version is, for plain floor constraints, the maximum of the declared floors. Upper bounds and exclusions still narrow the admissible set, but a newly published version is never adopted merely for being newer: it becomes eligible only when some manifest raises a floor to it.
The [exports] Section
The [exports] section controls which pipes are visible to consumers of the package.
Default visibility rules:
- Concepts are always public. Concepts are vocabulary — they are always accessible from outside the package.
- Pipes are private by default. A pipe not listed in
[exports]is an implementation detail, invisible to consumers. main_pipeis auto-exported. A pipe designated asmain_pipe— in the manifest's[package]section or in a bundle header — is exported by virtue of beingmain_pipe, whether or not it also appears in[exports]. The export surface of a package is the union of[exports]and its auto-exported main pipes.
Exports Table Structure
The [exports] section uses nested TOML tables that mirror the domain hierarchy. The domain path maps directly to the TOML table path:
[exports.legal]
pipes = ["classify_document"]
[exports.legal.contracts]
pipes = ["extract_clause", "analyze_nda", "compare_contracts"]
[exports.scoring]
pipes = ["compute_weighted_score"]
Each leaf table contains:
| Field | Type | Required | Description |
|---|---|---|---|
pipes |
array of strings | Yes | Pipe codes that are public from this domain. Each entry MUST be a valid pipe code (snake_case). |
Validation rules:
- Domain paths in
[exports]MUST be valid domain codes. - Domain paths in
[exports]MUST NOT start with a reserved domain segment (native,mthds,pipelex). - A domain MAY have both a
pipeslist and sub-domain tables (e.g.,[exports.legal]withpipesAND[exports.legal.contracts]).
Standalone Bundles (No Manifest)
A .mthds file without a METHODS.toml manifest is a standalone bundle. It behaves as an implicit local package with:
- No dependencies (beyond native concepts).
- All pipes treated as public (no visibility restrictions).
- No package address (not distributable).
This preserves the "single file = working method" experience for learning, prototyping, and simple projects.
A method with no manifest is either a directory a caller hands over, whose method tree is rooted at that directory, or a .mthds file named alone, whose method tree is that one file and nothing else, so a file reference from it to any file beside it is refused. See Method Tree: The Root.
Package Directory Structure
A package is a directory containing a METHODS.toml manifest and one or more .mthds bundle files. The directory layout follows a progressive enhancement principle — start minimal, add structure as needed.
Minimal package:
my_tool/
├── METHODS.toml
└── main.mthds
Full package:
legal_tools/
├── METHODS.toml
├── methods.lock
├── general_legal.mthds
├── contract_analysis.mthds
├── shareholder_agreements.mthds
├── scoring.mthds
├── requirements.txt
├── funcs/
│ └── scoring.py
├── templates/
│ └── nda_report.html
├── README.md
└── LICENSE
Rules:
METHODS.tomlMUST be at the directory root, which is the root of the package's method tree.methods.lockMUST be at the directory root, alongsideMETHODS.toml..mthdsfiles MAY be at the root or in subdirectories. A compliant implementation MUST load every.mthdsfile of the method tree, and only those: a bundle the membership rule excludes is not part of the package.- Every file of the method tree belongs to the package and travels with it, whether a step reads it, such as a Python module or a template, or it is carried for people and agents, such as a
README.md. - A single directory SHOULD contain one package. A repository MAY host several packages in subdirectories — a library repository — in which case each package is identified by its own manifest, and a specific package is located by manifest identity, never by directory path (see Distribution: Locating a Package Inside a Clone). A package in a directory below another package's root is out of that package's method tree, by the membership rule's nested-package default, so packages never hold one another.
- The directory name is not part of the package's identity and MAY differ from the manifest
name(see Name).
Manifest Discovery
When loading a .mthds bundle, a compliant implementation SHOULD discover the manifest by walking up from the bundle file's directory, or from the directory a caller hands over:
- Check the current directory for
METHODS.toml. - If not found, move to the parent directory.
- Stop when
METHODS.tomlis found, an entry named.gitof any kind is encountered (a directory, a file such as the one a submodule or a worktree holds, or a symbolic link, never followed), which is the entry that marks a nested repository under the membership rule, or the filesystem root is reached. - If no manifest is found, the bundle is treated as a standalone bundle (no package).
The directory where discovery finds METHODS.toml is the root of the method's tree. When discovery finds none, the root is the directory handed over, or, for a .mthds file named alone, the tree is that one file (see Method Tree: The Root).
Complete Manifest Example
[package]
name = "nda_analyzer"
address = "github.com/acme/legal-tools"
display_name = "Legal Tools"
version = "0.3.0"
description = "Legal document analysis and contract review methods."
authors = ["ACME Legal Tech <legal@acme.com>"]
license = "MIT"
mthds_version = ">=8.0.0"
main_pipe = "analyze_nda"
exclude = ["drafts/"]
[exports.legal]
pipes = ["classify_document"]
[exports.legal.contracts]
pipes = ["extract_clause", "analyze_nda", "compare_contracts"]
[exports.scoring]
pipes = ["compute_weighted_score"]