# MTHDS > Official documentation for the MTHDS open standard: to give AI methods to AI agents. # Home ## Overview # MTHDS: the language of executable AI methods **TLDR** MTHDS is a declarative language for defining **AI methods**: discrete, reusable units of cognitive work like extraction, analysis, synthesis, generation. It is built on TOML and introduces two primitives: **concepts** (semantically typed data named after real domain things) and **pipes** (deterministic orchestration steps with explicit typed inputs and outputs). Pipes can invoke LLMs, VLMs, OCR, and image generation models, with built-in *structured generation*. The typing is **conceptual**: `NonCompeteClause` is a refinement of `ContractClause`, meaning pipes compose wherever types are compatible, without manual glue code, because the types carry *business meaning* rather than just structure. Methods are **executable and composable** like Unix tools: a method can be saved as a CLI command, combined with others using standard Unix pipes, or invoked directly by Claude Code. Methods can also be published as packages, used as templates to customize, or called as components from other methods. Teams can share some methods openly and keep their secret sauce private. The standard ships with a **Claude Code plugin** that lets Claude write, modify, and compose methods on your behalf. This makes MTHDS **agent-first by design**: a domain expert who can describe what they need in plain language can have Claude author the method, which then runs consistently, is testable, and lives in version control. Where agent skills handle open-ended tasks, methods handle the parts that benefit from being **explicit, versioned, and validated**. And unlike skills, methods are *executable outside the scope of an agent entirely*. - Hub: [mthds.sh](https://mthds.sh) - Spec: [github.com/mthds-ai/mthds](https://github.com/mthds-ai/mthds) - Reference implementation: [github.com/Pipelex/pipelex](https://github.com/Pipelex/pipelex) - Agent plugins: [github.com/mthds-ai/mthds-plugins](https://github.com/mthds-ai/mthds-plugins) - VS Code extension: [go.pipelex.com/vscode](https://go.pipelex.com/vscode)
]` sub-tables.
### Concept Blueprint Fields
When using the structured form `[concept.]`, the following fields are available:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | Yes | Human-readable description of the concept. |
| `structure` | table or string | No | Field definitions for the concept. If a string, it is a shorthand description (equivalent to a simple declaration). If a table, each key is a field name mapped to a field blueprint. |
| `refines` | string | No | A concept reference indicating that this concept is a specialization of another concept. |
**Validation rules:**
- `refines` and `structure` MUST NOT both be present on the same concept. A concept either refines another concept or defines its own structure, not both.
- `refines`, if present, MUST be a valid concept reference: either a bare concept code (`PascalCase`) or a domain-qualified reference (`domain.ConceptCode`). Cross-package references (`alias->domain.ConceptCode`) are also valid.
- Concept codes MUST be `PascalCase`, matching the pattern `[A-Z][a-zA-Z0-9]*`.
- Concept codes MUST NOT collide with native concept codes (see [Native Concepts](#native-concepts)).
### Concept Refinement
Refinement establishes a specialization relationship between concepts. A concept that refines another inherits its semantic meaning and can be used anywhere the parent concept is expected.
```toml
[concept.NonCompeteClause]
description = "A non-compete clause in an employment contract"
refines = "ContractClause"
```
The `refines` field accepts:
- A bare concept code: `"ContractClause"` — resolved within the current bundle's domain.
- A domain-qualified reference: `"legal.ContractClause"` — resolved within the current package.
- A cross-package reference: `"acme_legal->legal.contracts.NonDisclosureAgreement"` — resolved from a dependency.
### Concept Structure Fields
When `structure` is a table, each key is a field name and each value is a field blueprint. Field names MUST NOT start with an underscore (`_`), as these are reserved for internal use. Field names MUST NOT collide with reserved field names (Pydantic model attributes and internal metadata fields).
#### Field Blueprint
Each field in a concept structure is defined by a field blueprint:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | Yes | Human-readable description of the field. |
| `type` | string | Conditional | The field type. Required unless `choices` is provided. |
| `required` | boolean | No | Whether the field is required. Default: `false`. |
| `default_value` | any | No | Default value for the field. Must match the declared type. |
| `choices` | array of strings | No | Fixed set of allowed string values. When `choices` is set, `type` MUST be omitted (the type is implicitly an enum of the given choices). |
| `key_type` | string | Conditional | Key type for `dict` fields. Required when `type = "dict"`. |
| `value_type` | string | Conditional | Value type for `dict` fields. Required when `type = "dict"`. |
| `item_type` | string | No | Item type for `list` fields. When set to `"concept"`, `item_concept_ref` is required. |
| `concept_ref` | string | Conditional | Concept reference for `concept`-typed fields. Required when `type = "concept"`. |
| `item_concept_ref` | string | Conditional | Concept reference for list items when `item_type = "concept"`. |
#### Field Types
The `type` field accepts the following values:
| Type | Description | `default_value` type |
|------|-------------|---------------------|
| `text` | A string value. | `string` |
| `integer` | A whole number. | `integer` |
| `number` | A numeric value (integer or floating-point). | `integer` or `float` |
| `boolean` | A true/false value. | `boolean` |
| `date` | A date value. | `datetime` |
| `list` | An ordered collection. Use `item_type` to specify element type. | `array` |
| `dict` | A key-value mapping. Requires `key_type` and `value_type`. | `table` |
| `concept` | A reference to another concept. Requires `concept_ref`. Cannot have `default_value`. | *(not allowed)* |
When `type` is omitted and `choices` is provided, the field is an enumeration field. The value MUST be one of the strings in the `choices` array.
**Validation rules for field types:**
- `type = "dict"`: `key_type` and `value_type` MUST both be non-empty.
- `type = "concept"`: `concept_ref` MUST be set. `default_value` MUST NOT be set.
- `type = "list"` with `item_type = "concept"`: `item_concept_ref` MUST be set.
- `item_concept_ref` MUST NOT be set unless `item_type = "concept"`.
- `concept_ref` MUST NOT be set unless `type = "concept"`.
- If `choices` is provided and `type` is omitted, `default_value` (if present) MUST be one of the values in `choices`.
- If both `type` and `default_value` are set, the runtime type of `default_value` MUST match the declared `type`.
**Example — concept with all field types:**
```toml
[concept.CandidateProfile]
description = "A candidate's profile for job matching"
[concept.CandidateProfile.structure]
full_name = { type = "text", description = "Full name", required = true }
years_experience = { type = "integer", description = "Years of professional experience" }
gpa = { type = "number", description = "Grade point average" }
is_active = { type = "boolean", description = "Whether actively looking", default_value = true }
graduation_date = { type = "date", description = "Date of graduation" }
skills = { type = "list", item_type = "text", description = "List of skills" }
metadata = { type = "dict", key_type = "text", value_type = "text", description = "Additional metadata" }
seniority_level = { description = "Seniority level", choices = ["junior", "mid", "senior", "lead"] }
address = { type = "concept", concept_ref = "Address", description = "Home address" }
references = { type = "list", item_type = "concept", item_concept_ref = "ContactInfo", description = "Professional references" }
```
## Native Concepts
Native concepts are built-in types that are always available in every bundle without declaration. They belong to the reserved `native` domain.
| Code | Qualified Reference | Description |
|------|-------------------|-------------|
| `Dynamic` | `native.Dynamic` | A dynamically-typed value. |
| `Text` | `native.Text` | A text string. |
| `Image` | `native.Image` | An image (binary). |
| `Document` | `native.Document` | A document (e.g., PDF, web page). |
| `Html` | `native.Html` | HTML content. |
| `TextAndImages` | `native.TextAndImages` | Combined text and image content. |
| `Number` | `native.Number` | A numeric value. |
| `Page` | `native.Page` | A single page extracted from a document. |
| `JSON` | `native.JSON` | A JSON value. |
| `SearchResult` | `native.SearchResult` | A web search result with answer and sources. |
| `Anything` | `native.Anything` | Accepts any type. |
Native concepts MAY be referenced by bare code (`Text`, `Image`) or by qualified reference (`native.Text`, `native.Image`). Bare native concept codes always take priority during resolution.
A bundle MUST NOT declare a concept with the same code as a native concept. A compliant implementation MUST reject such declarations.
## Pipe Definitions
Pipes are typed transformations. Each pipe has a typed signature: it declares what concepts it accepts as input and what concept it produces as output.
### Common Pipe Fields
All pipe types share these base fields:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | Yes | The pipe type. Determines which category and additional fields are available. |
| `description` | string | Yes | Human-readable description of what this pipe does. |
| `inputs` | table | No | Input declarations. Keys are input names (`snake_case`), values are concept references with optional multiplicity. |
| `output` | string | Yes | The output concept reference with optional multiplicity. |
**Pipe codes:**
- Pipe codes are the keys in `[pipe.]` tables.
- Pipe codes MUST be `snake_case`, matching the pattern `[a-z][a-z0-9_]*`.
**Input names:**
- Input names MUST be `snake_case`.
- Dotted input names are allowed for nested field access (e.g., `my_input.field_name`), where each segment MUST be `snake_case`.
**Concept references in inputs and output:**
Concept references in `inputs` and `output` support an optional multiplicity suffix:
| Syntax | Meaning |
|--------|---------|
| `ConceptName` | A single instance. |
| `ConceptName[]` | A variable-length list (runtime determines count). |
| `ConceptName[N]` | A fixed-length list of exactly N items (N ≥ 1). |
Concept references MAY be bare codes (`Text`), domain-qualified (`legal.ContractClause`), or cross-package qualified (`alias->domain.ConceptCode`).
**Example:**
```toml
[pipe.analyze_contract]
type = "PipeLLM"
description = "Analyze a legal contract and extract key clauses"
output = "ContractClause[5]"
[pipe.analyze_contract.inputs]
contract_text = "Text"
```
### Pipe Types
MTHDS defines pipe types in two categories:
**Operators** — pipes that perform a single transformation:
| Type | Value | Description |
|------|-------|-------------|
| PipeLLM | `"PipeLLM"` | Generates output using a large language model. |
| PipeStructure | `"PipeStructure"` | Turns text into a structured concept using a large language model. |
| PipeFunc | `"PipeFunc"` | Calls a registered Python function. |
| PipeImgGen | `"PipeImgGen"` | Generates images using an image generation model. |
| PipeExtract | `"PipeExtract"` | Extracts structured content from documents. |
| PipeSearch | `"PipeSearch"` | Searches the web and returns structured results. |
| PipeCompose | `"PipeCompose"` | Composes output from templates or constructs. |
**Controllers** — pipes that orchestrate other pipes:
| Type | Value | Description |
|------|-------|-------------|
| PipeSequence | `"PipeSequence"` | Executes a series of pipes in order. |
| PipeParallel | `"PipeParallel"` | Executes pipes concurrently. |
| PipeCondition | `"PipeCondition"` | Routes execution based on a condition. |
| PipeBatch | `"PipeBatch"` | Maps a pipe over each item in a list. |
## Operator: PipeLLM
Generates output by invoking a large language model with a prompt.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeLLM"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `prompt` | string | No | The LLM prompt template. Supports Jinja2 syntax and the `@variable` / `$variable` shorthand. |
| `system_prompt` | string | No | System prompt for the LLM. If omitted, the bundle-level `system_prompt` is used (if any). |
| `model` | string or table | No | Model identifier, model reference (see [Model References](../language/model-references.md)), or an inline [LLM settings](#inline-llm-settings) table. |
| `model_to_structure` | string or table | No | Model used for structuring the LLM output into the declared concept. Accepts the same forms as `model`. |
| `structuring_method` | string | No | Directive controlling how the output is structured. Values: `"direct"` (single LLM call producing JSON) or `"preliminary_text"` (the runtime first produces text, then structures it as a second step). The standard does not prescribe HOW the runtime implements `"preliminary_text"`. |
**Prompt template syntax:**
- `{{ variable_name }}` — standard Jinja2 variable substitution.
- `@variable_name` — shorthand, preprocessed to Jinja2 syntax.
- `$variable_name` — shorthand, preprocessed to Jinja2 syntax.
- Dotted paths are supported: `{{ doc_request.document_type }}`, `@doc_request.priority`.
**Validation rules:**
- Every variable referenced in `prompt` and `system_prompt` MUST correspond to a declared input (by root name). Internal variables starting with `_` and the special names `preliminary_text` and `place_holder` are excluded from this check.
- Every declared input MUST be referenced by at least one variable in `prompt` or `system_prompt`. Unused inputs are rejected.
**Example:**
```toml
[pipe.analyze_cv]
type = "PipeLLM"
description = "Analyze a CV to extract key professional information"
output = "CVAnalysis"
model = "$writing-factual"
system_prompt = """
You are an expert HR analyst specializing in CV evaluation.
"""
prompt = """
Analyze the following CV and extract the candidate's key professional information.
@cv_pages
"""
[pipe.analyze_cv.inputs]
cv_pages = "Page"
```
### Inline LLM Settings
When the `model` field is a table instead of a string, it defines inline model settings using the `LLMSetting` structure:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `model` | string | Yes | The model handle (e.g., `"claude-4.5-sonnet"`). |
| `temperature` | number | Yes | Sampling temperature. Range: 0–1. |
| `max_tokens` | integer, `"auto"`, or null | No | Maximum tokens for the response. `"auto"` lets the model choose. |
| `image_detail` | string | No | Image detail level for vision inputs. Values: `high`, `low`, `auto`. |
| `prompting_target` | string | No | Target provider for prompt formatting. Values: `openai`, `anthropic`, `mistral`, `gemini`, `fal`. |
| `reasoning_effort` | string | No | Level of reasoning effort. Values: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. `xhigh` sits between `high` and `max` and maps to provider-specific xhigh values where supported. |
| `reasoning_budget` | integer | No | Token budget for reasoning. Must be > 0. |
| `description` | string | No | Human-readable description of this model configuration. |
**Validation rules:**
- `reasoning_effort` and `reasoning_budget` MUST NOT both be set on the same inline LLM settings table.
**Example — inline LLM settings:**
```toml
[pipe.analyze_cv]
type = "PipeLLM"
description = "Analyze a CV"
output = "CVAnalysis"
prompt = "Analyze: @cv_pages"
model = { model = "claude-4.5-sonnet", temperature = 0.1, max_tokens = 4096 }
[pipe.analyze_cv.inputs]
cv_pages = "Page"
```
## Operator: PipeStructure
Turns text into a structured concept matching the declared output schema. The standard does not prescribe how a runtime achieves this; a typical implementation uses an LLM call.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeStructure"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | Yes | MUST contain exactly one entry. |
| `output` | string | Yes | The target structured concept reference, with optional multiplicity. MUST NOT be `Text` or a concept that refines `Text`. |
| `model` | string or table | No | Model identifier, model reference (see [Model References](../language/model-references.md)), or an inline [LLM settings](#inline-llm-settings) table. |
**Validation rules:**
- `inputs` MUST contain exactly one entry. The single input concept MUST be `Text` or a concept that refines `Text`.
- `output` MUST NOT be `Text` and MUST NOT be a concept that refines `Text`.
- `output` MAY use multiplicity (`Foo`, `Foo[]`, `Foo[N]`).
- `PipeStructure` MUST NOT accept image or document inputs. Use an upstream extraction step to produce text first.
**Example:**
```toml
[pipe.structure_review]
type = "PipeStructure"
description = "Turn a free-form review into a RestaurantReview"
inputs = { review_text = "Text" }
output = "RestaurantReview"
```
**Example — with an explicit structuring model:**
```toml
[pipe.structure_review_premium]
type = "PipeStructure"
description = "Use a stronger model for tricky structurings"
inputs = { review_text = "Text" }
output = "RestaurantReview"
model = "@default-premium"
```
## Operator: PipeFunc
Calls a registered Python function.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeFunc"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `function_name` | string | Yes | The fully-qualified name of the Python function to call. |
**Example:**
```toml
[pipe.capitalize_text]
type = "PipeFunc"
description = "Capitalize the input text"
inputs = { text = "Text" }
output = "Text"
function_name = "my_package.text_utils.capitalize"
```
## Operator: PipeImgGen
Generates images using an image generation model. The pipe carries a required `prompt` string template (and an optional `negative_prompt` template); it does not take a dedicated prompt concept as input. Declared `inputs` are injected into the `prompt` at runtime: `Text` inputs are interpolated into the prompt text, while `Image` inputs (a single image or a list) are referenced in the prompt and injected as reference images — each becomes an `[Image N]` token in the rendered text and is passed to the generator alongside it, enabling image-to-image, reference-image, and image-editing generation.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeImgGen"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `prompt` | string | Yes | The image generation prompt template. Supports Jinja2 and `$variable` shorthand; declared inputs are injected into it. |
| `negative_prompt` | string | No | An optional prompt template describing what to avoid in the generated image. |
| `model` | string or table | No | Model identifier, model reference (see [Model References](../language/model-references.md)), or an inline [image generation settings](#inline-image-generation-settings) table. |
| `aspect_ratio` | string | No | Desired aspect ratio. Values: `square`, `landscape_4_3`, `landscape_3_2`, `landscape_16_9`, `landscape_21_9`, `portrait_3_4`, `portrait_2_3`, `portrait_9_16`, `portrait_9_21`. |
| `is_raw` | boolean | No | Whether to use raw mode (less post-processing). |
| `seed` | integer or `"auto"` | No | Random seed for reproducibility. `"auto"` lets the model choose. |
| `background` | string | No | Background setting. Values: `transparent`, `opaque`, `auto`. |
| `output_format` | string | No | Image output format. Values: `png`, `jpeg`, `webp`. |
**Validation rules:**
- Every variable referenced in `prompt` or `negative_prompt` MUST correspond to a declared input.
- `output` MUST resolve to an `Image`-compatible concept.
- Any input referenced as a reference image in the `prompt` or `negative_prompt` MUST resolve to an `Image`-compatible concept (single or list).
**Example:**
```toml
[pipe.generate_portrait]
type = "PipeImgGen"
description = "Generate a portrait image from a description"
inputs = { description = "Text" }
output = "Image"
prompt = "A professional portrait: $description"
model = "$gen-image-testing"
```
**Example with a reference image (image-to-image):**
```toml
[pipe.restyle_photo]
type = "PipeImgGen"
description = "Restyle a source photo following a textual instruction"
inputs = { source = "Image", instruction = "Text" }
output = "Image"
prompt = "Restyle this image: $source. $instruction"
```
### Inline Image Generation Settings
When the `model` field is a table instead of a string, it defines inline model settings using the `ImgGenSetting` structure:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `model` | string | Yes | The model handle. |
| `quality` | string | No | Image quality. Values: `low`, `medium`, `high`. |
| `nb_steps` | integer | No | Number of generation steps. Must be > 0. |
| `guidance_scale` | number | No | Guidance scale for generation. Must be > 0. |
| `is_moderated` | boolean | No | Whether to apply content moderation. Default: `false`. |
| `safety_tolerance` | integer | No | Safety tolerance level. Range: 1–6. |
| `description` | string | No | Human-readable description of this model configuration. |
**Validation rules:**
- `quality` and `nb_steps` MUST NOT both be set on the same inline image generation settings table.
**Example — inline image generation settings:**
```toml
[pipe.generate_portrait]
type = "PipeImgGen"
description = "Generate a portrait image"
inputs = { description = "Text" }
output = "Image"
prompt = "A professional portrait: $description"
aspect_ratio = "portrait_3_4"
model = { model = "flux-pro", quality = "high" }
```
## Operator: PipeExtract
Extracts structured content from documents (e.g., PDF, web pages).
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeExtract"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | Yes | MUST contain exactly one input. |
| `output` | string | Yes | MUST be `"Page[]"`. |
| `model` | string or table | No | Model identifier, model reference (see [Model References](../language/model-references.md)), or an inline [extract settings](#inline-extract-settings) table. |
| `max_page_images` | integer | No | Maximum number of page images to process. |
| `page_image_captions` | boolean | No | Whether to generate captions for page images. |
| `page_views` | boolean | No | Whether to generate page views. |
| `page_views_dpi` | integer | No | DPI for page view rendering. |
| `render_js` | boolean | No | Web-page extraction only: render JavaScript before fetching the page content. Honored by backends that support headless rendering. Default: `false`. |
| `include_raw_html` | boolean | No | Web-page extraction only: include the fetched HTML in each extracted `Page`'s `raw_html` field. Default: `false`. |
**Validation rules:**
- `inputs` MUST contain exactly one entry. The input concept SHOULD be `Document` or a concept that refines `Document` or `Image`.
- `output` MUST be `"Page[]"` (a variable-length list of `Page`).
- When the document URL is a web page, PipeExtract fetches and extracts the page content. `render_js` and `include_raw_html` apply only to this case; backends that target local documents (PDFs, images) MAY ignore them.
**Example:**
```toml
[pipe.extract_cv]
type = "PipeExtract"
description = "Extract text content from a CV PDF document"
inputs = { cv_pdf = "Document" }
output = "Page[]"
model = "@default-text-from-pdf"
```
### Inline Extract Settings
When the `model` field is a table instead of a string, it defines inline model settings using the `ExtractSetting` structure:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `model` | string | Yes | The model handle. |
| `max_nb_images` | integer | No | Maximum number of images to extract. Must be >= 0. |
| `image_min_size` | integer | No | Minimum image size in pixels. Must be >= 0. |
| `description` | string | No | Human-readable description of this model configuration. |
**Example — inline extract settings:**
```toml
[pipe.extract_cv]
type = "PipeExtract"
description = "Extract text content from a CV PDF document"
inputs = { cv_pdf = "Document" }
output = "Page[]"
model = { model = "gpt-4.1", max_nb_images = 10, image_min_size = 100 }
```
## Operator: PipeSearch
Searches the web using a search provider and returns structured results.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeSearch"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | MUST be `SearchResult` or a concept that refines `SearchResult`. |
| `prompt` | string | Yes | The search query template. Supports Jinja2 syntax and the `@variable` / `$variable` shorthand. |
| `model` | string or table | No | Model identifier, model reference (see [Model References](../language/model-references.md)), or an inline [search settings](#inline-search-settings) table. |
| `from_date` | string | No | Start date filter in ISO 8601 format (YYYY-MM-DD). Only return results from this date onwards. |
| `to_date` | string | No | End date filter in ISO 8601 format (YYYY-MM-DD). Only return results up to this date. |
| `include_domains` | array of strings | No | Restrict search to these domains only (e.g., `["reuters.com", "bbc.com"]`). |
| `exclude_domains` | array of strings | No | Exclude results from these domains. |
**Validation rules:**
- Every variable referenced in `prompt` MUST correspond to a declared input.
- `output` MUST be `SearchResult` or a concept that refines `SearchResult`.
**Example:**
```toml
[pipe.search_topic]
type = "PipeSearch"
description = "Search the web for information about a topic"
inputs = { topic = "Text" }
output = "SearchResult"
model = "$standard"
prompt = "What is $topic?"
```
**Example — with date and domain filters:**
```toml
[pipe.search_recent_from_sources]
type = "PipeSearch"
description = "Search specific sources for recent news"
inputs = { topic = "Text" }
output = "SearchResult"
model = "$standard"
prompt = "What are the latest developments about $topic?"
from_date = "2026-01-01"
include_domains = ["reuters.com", "apnews.com", "bbc.com"]
```
### Inline Search Settings
When the `model` field is a table instead of a string, it defines inline model settings using the `SearchSetting` structure:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `model` | string | Yes | The model handle. |
| `include_images` | boolean | No | Whether to include images in results. Default: `false`. |
| `include_inline_citations` | boolean | No | Whether to include inline citations. Default: `true`. |
| `max_results` | integer or null | No | Maximum number of results. Must be ≥ 1. |
| `description` | string | No | Human-readable description of this model configuration. |
**Example — inline search settings:**
```toml
[pipe.deep_search]
type = "PipeSearch"
description = "Deep research on a topic"
inputs = { topic = "Text" }
output = "SearchResult"
prompt = "What are the main details about $topic?"
model = { model = "linkup-deep", include_images = false }
```
## Operator: PipeCompose
Composes output by assembling data from working memory using either a template or a construct. Exactly one of `template` or `construct` MUST be provided.
### Template Mode
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeCompose"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | MUST be a single concept (no multiplicity). |
| `template` | string or table | Yes (if no `construct`) | A Jinja2 template string, or a template blueprint table with `template`, `category`, `templating_style`, and `extra_context` fields. |
When `template` is a string, it is a Jinja2 template rendered with the input variables. When `template` is a table, it MUST contain a `template` field (string) and a `category` field, and MAY contain `templating_style` and `extra_context`.
**Template shorthand syntax:**
MTHDS defines three shorthand patterns that a compliant preprocessor MUST expand before Jinja2 rendering:
| Pattern | Expansion | Description |
|---------|-----------|-------------|
| `$name` | `{{ name|format() }}` | Inline substitution with formatting. |
| `@name` | `{{ name|tag("name") }}` | Block insertion with tagging. |
| `@?name` | `{% if name %}{{ name|tag("name") }}{% endif %}` | Conditional block insertion (renders only if truthy). |
**Rules:**
- A shorthand pattern MUST NOT match when the character immediately following `$`, `@`, or `@?` is a digit (`0`–`9`). This prevents dollar amounts (e.g., `$100`) and version-like strings (e.g., `@2.0`) from being treated as variables.
- Dotted paths are supported: `$user.name`, `@doc.summary`, `@?extra.notes`. Each segment of the dotted path MUST be a valid identifier.
- When a matched name ends with a `.` (dot), the preprocessor MUST strip the trailing dot from the variable name and place it after the expanded expression (treating it as sentence punctuation).
- Raw Jinja2 syntax (`{{ }}`, `{% %}`) MUST always be accepted alongside the shorthands.
These shorthands apply to the `template` field of PipeCompose, the `prompt` and `system_prompt` fields of PipeLLM, the `prompt` and `negative_prompt` fields of PipeImgGen, and the `prompt` field of PipeSearch. See [Pipes — Operators: Template Mode](../language/pipes-operators.md#template-mode) for the full reference on categories and filters.
**Template blueprint fields (table form):**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `template` | string | Yes | The Jinja2 template string. |
| `category` | string | Yes | Template category. Values: `basic`, `expression`, `html`, `markdown`, `mermaid`, `llm_prompt`, `img_gen_prompt`. |
| `templating_style` | table | No | Rendering style configuration. See [Templating Style](#templating-style) below. |
| `extra_context` | table | No | Additional context variables for template rendering. |
#### Templating Style
The `templating_style` field controls how template output is formatted, particularly useful for templates that produce prompts for different LLM providers.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tag_style` | string | Yes | How variables are tagged in output. Values: `no_tag`, `ticks`, `xml`, `square_brackets`. |
| `text_format` | string | No | Output text format. Values: `plain`, `markdown`, `html`, `json`. Default: `plain`. |
**Validation rules (template mode):**
- Every variable referenced in the template MUST correspond to a declared input.
- `output` MUST NOT use multiplicity brackets (`[]` or `[N]`).
### Construct Mode
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeCompose"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | MUST be a single concept (no multiplicity). |
| `construct` | table | Yes (if no `template`) | A field-by-field composition blueprint. |
The `construct` table defines how each field of the output concept is composed. Each key is a field name, and the value defines the composition method:
| Value form | Method | Description |
|------------|--------|-------------|
| Literal (`string`, `integer`, `float`, `boolean`, `array`) | Fixed | The field value is the literal. |
| `{ from = "path" }` | Variable reference | The field value comes from a variable in working memory. `path` is a dotted path (e.g., `"match_analysis.score"`). |
| `{ from = "path", list_to_dict_keyed_by = "attr" }` | Variable reference with transform | Converts a list to a dict keyed by the named attribute. |
| `{ template = "..." }` | Template | The field value is rendered from a Jinja2 template string. |
| Nested table (no `from` or `template` key) | Nested construct | The field is recursively composed from a nested construct. |
**Validation rules (construct mode):**
- The root variable of every `from` path and every template variable MUST correspond to a declared input.
- `from` and `template` are mutually exclusive within a single field definition.
**Example — construct mode:**
```toml
[pipe.compose_interview_sheet]
type = "PipeCompose"
description = "Compose the final interview sheet"
inputs = { match_analysis = "MatchAnalysis", interview_questions = "InterviewQuestion[]" }
output = "InterviewSheet"
[pipe.compose_interview_sheet.construct]
overall_match_score = { from = "match_analysis.overall_match_score" }
matching_skills = { from = "match_analysis.matching_skills" }
missing_skills = { from = "match_analysis.missing_skills" }
questions = { from = "interview_questions" }
```
## Controller: PipeSequence
Executes a series of sub-pipes in order. The output of each step is added to working memory and can be consumed by subsequent steps.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeSequence"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `steps` | array of tables | Yes | Ordered list of sub-pipe invocations. MUST contain at least one step. |
Each step is a **sub-pipe blueprint**:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pipe` | string | Yes | Pipe reference (bare, domain-qualified, or package-qualified). |
| `result` | string | No | Name under which the step's output is stored in working memory. |
| `nb_output` | integer | No | Expected number of output items. Mutually exclusive with `multiple_output`. |
| `multiple_output` | boolean | No | Whether to expect multiple output items. Mutually exclusive with `nb_output`. |
| `batch_over` | string | No | Working memory variable to iterate over (inline batch). Requires `batch_as`. |
| `batch_as` | string | No | Name for each item during inline batch iteration. Requires `batch_over`. |
**Validation rules:**
- `steps` MUST contain at least one entry.
- `nb_output` and `multiple_output` MUST NOT both be set on the same step.
- `batch_over` and `batch_as` MUST either both be present or both be absent.
- `batch_over` and `batch_as` MUST NOT be the same value.
**Example:**
```toml
[pipe.process_document]
type = "PipeSequence"
description = "Full document processing pipeline"
inputs = { document = "Document" }
output = "AnalysisResult"
steps = [
{ pipe = "extract_pages", result = "pages" },
{ pipe = "analyze_content", result = "analysis" },
{ pipe = "generate_summary", result = "summary" },
]
```
## Controller: PipeParallel
Executes multiple sub-pipes concurrently. Each branch operates independently.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeParallel"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `branches` | array of tables | Yes | List of sub-pipe invocations to execute concurrently. |
| `add_each_output` | boolean | No | If `true`, each branch's output is individually added to working memory under its `result` name. Default: `false`. |
| `combined_output` | string | No | Concept reference for a combined output that merges all branch results. |
**Validation rules:**
- At least one of `add_each_output` or `combined_output` MUST be set (otherwise the pipe produces no output).
- `combined_output`, if present, MUST be a valid concept reference.
- Each branch follows the same sub-pipe blueprint format as `PipeSequence` steps.
**Example:**
```toml
[pipe.extract_documents]
type = "PipeParallel"
description = "Extract text from both CV and job offer concurrently"
inputs = { cv_pdf = "Document", job_offer_pdf = "Document" }
output = "Page[]"
add_each_output = true
branches = [
{ pipe = "extract_cv", result = "cv_pages" },
{ pipe = "extract_job_offer", result = "job_offer_pages" },
]
```
## Controller: PipeCondition
Routes execution to different pipes based on an evaluated condition.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeCondition"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | No | — |
| `output` | string | Yes | — |
| `expression_template` | string | Conditional | A Jinja2 template that evaluates to a string matching an outcome key. Exactly one of `expression_template` or `expression` MUST be provided. |
| `expression` | string | Conditional | A static expression string. Exactly one of `expression_template` or `expression` MUST be provided. |
| `outcomes` | table | Yes | Maps outcome strings to pipe references. MUST have at least one entry. |
| `default_outcome` | string | Yes | The pipe reference (or special outcome) to use when no outcome key matches. |
| `add_alias_from_expression_to` | string | No | If set, stores the evaluated expression value in working memory under this name. |
**Special outcomes:**
Certain string values in `outcomes` values and `default_outcome` have special meaning and are not treated as pipe references:
| Value | Meaning |
|-------|---------|
| `"fail"` | Abort execution with an error. |
| `"continue"` | Skip this branch and continue without executing a sub-pipe. |
**Example:**
```toml
[pipe.route_by_document_type]
type = "PipeCondition"
description = "Route processing based on document type"
inputs = { doc_request = "DocumentRequest" }
output = "Text"
expression_template = "{{ doc_request.document_type }}"
default_outcome = "continue"
[pipe.route_by_document_type.outcomes]
technical = "process_technical"
business = "process_business"
legal = "process_legal"
```
## Controller: PipeBatch
Maps a single pipe over each item in a list input, producing a list output.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"PipeBatch"` | Yes | — |
| `description` | string | Yes | — |
| `inputs` | table | Yes | MUST include an entry whose name matches `input_list_name`. |
| `output` | string | Yes | — |
| `branch_pipe_code` | string | Yes | The pipe reference to invoke for each item. |
| `input_list_name` | string | Yes | The name of the input that contains the list to iterate over. |
| `input_item_name` | string | Yes | The name under which each individual item is passed to the branch pipe. |
**Validation rules:**
- `input_list_name` MUST exist as a key in `inputs`.
- `input_item_name` MUST NOT be empty.
- `input_item_name` MUST NOT equal `input_list_name`.
- `input_item_name` MUST NOT equal any key in `inputs`.
**Example:**
```toml
[pipe.batch_generate_jokes]
type = "PipeBatch"
description = "Generate a joke for each topic"
inputs = { topics = "Topic[]" }
output = "Joke[]"
branch_pipe_code = "generate_joke"
input_list_name = "topics"
input_item_name = "topic"
```
## Pipe Reference Syntax
Every location in a `.mthds` file that references another pipe supports three forms:
| Form | Syntax | Example | Resolution |
|------|--------|---------|------------|
| Bare | `pipe_code` | `"extract_clause"` | Resolved within the current bundle and its domain. |
| Domain-qualified | `domain.pipe_code` | `"legal.contracts.extract_clause"` | Resolved within the named domain of the current package. |
| Package-qualified | `alias->domain.pipe_code` | `"docproc->extraction.extract_text"` | Resolved in the named domain of the dependency identified by the alias. |
Pipe references appear in:
- `steps[].pipe` (PipeSequence)
- `branches[].pipe` (PipeParallel)
- `outcomes` values (PipeCondition)
- `default_outcome` (PipeCondition)
- `branch_pipe_code` (PipeBatch)
Pipe *definitions* (the `[pipe.]` table keys) are always bare `snake_case` names. Namespacing applies only to pipe *references*.
## Concept Reference Syntax
Every location that references a concept supports three forms, symmetric with pipe references:
| Form | Syntax | Example | Resolution |
|------|--------|---------|------------|
| Bare | `ConceptCode` | `"ContractClause"` | Resolved in order: native concepts → current bundle → same domain. |
| Domain-qualified | `domain.ConceptCode` | `"legal.contracts.NonCompeteClause"` | Resolved within the named domain of the current package. |
| Package-qualified | `alias->domain.ConceptCode` | `"acme->legal.ContractClause"` | Resolved in the named domain of the dependency identified by the alias. |
The disambiguation between concepts and pipes in a domain-qualified reference relies on casing:
- `snake_case` final segment → pipe code
- `PascalCase` final segment → concept code
Concept references appear in:
- `inputs` values
- `output`
- `refines`
- `concept_ref` and `item_concept_ref` in structure field blueprints
- `combined_output` (PipeParallel)
## Complete Bundle Example
```toml
domain = "joke_generation"
description = "Generating one-liner jokes from topics"
main_pipe = "generate_jokes_from_topics"
[concept.Topic]
description = "A subject or theme that can be used as the basis for a joke."
refines = "Text"
[concept.Joke]
description = "A humorous one-liner intended to make people laugh."
refines = "Text"
[pipe.generate_jokes_from_topics]
type = "PipeSequence"
description = "Generate 3 joke topics and create a joke for each"
output = "Joke[]"
steps = [
{ pipe = "generate_topics", result = "topics" },
{ pipe = "batch_generate_jokes", result = "jokes" },
]
[pipe.generate_topics]
type = "PipeLLM"
description = "Generate 3 distinct topics suitable for jokes"
output = "Topic[3]"
prompt = "Generate 3 distinct and varied topics for crafting one-liner jokes."
[pipe.batch_generate_jokes]
type = "PipeBatch"
description = "Generate a joke for each topic"
inputs = { topics = "Topic[]" }
output = "Joke[]"
branch_pipe_code = "generate_joke"
input_list_name = "topics"
input_item_name = "topic"
[pipe.generate_joke]
type = "PipeLLM"
description = "Write a clever one-liner joke about the given topic"
inputs = { topic = "Topic" }
output = "Joke"
prompt = "Write a clever one-liner joke about $topic. Be concise and witty."
```
## METHODS.toml Format
# 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. **Not yet implemented.** |
| `[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](#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](https://semver.org/) (`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 "`). 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_]*`). MUST reference a pipe declared in the `[exports]` section. See [Main Pipe](#main-pipe). |
### Address Format
The package address is the globally unique identifier for the package. It doubles as the fetch location for VCS-based distribution.
**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 package directory name MUST match the `name` field exactly. A compliant tool MUST reject a package whose directory name does not match its manifest name.
**Example:**
```toml
# Inside nda_analyzer/METHODS.toml
[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:**
```toml
[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](https://semver.org/):
```
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](#version-constraint-syntax)).
The current MTHDS standard version is `1.0.0`.
### 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 slug or address:
```bash
mthds run method nda_analyzer
mthds run method github.com/acme/legal-tools
```
**Constraints:**
- The value MUST be a valid `snake_case` pipe code (matching `[a-z][a-z0-9_]*`).
- The referenced pipe MUST be declared in the `[exports]` section. A manifest that sets `main_pipe` to a pipe not listed in exports is invalid.
- When `main_pipe` is not set, the package has no default entry point. It can still be consumed as a library by importing specific pipes.
**Example:**
```toml
[package]
name = "nda_analyzer"
address = "github.com/acme/legal-tools"
version = "0.3.0"
main_pipe = "analyze_nda"
```
## The `[dependencies]` Section
> **Not yet implemented.** Dependencies between packages are planned but not yet supported. The specification below describes the intended behavior for a future release.
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).
```toml
[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](#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:**
```toml
[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` | `>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.*`.
## 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_pipe` must be exported.** If a package declares a `main_pipe`, that pipe MUST appear in the `[exports]` section.
### 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:
```toml
[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 `pipes` list and sub-domain tables (e.g., `[exports.legal]` with `pipes` AND `[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.
## 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
├── README.md
└── LICENSE
```
**Rules:**
- `METHODS.toml` MUST be at the directory root.
- `methods.lock` MUST be at the directory root, alongside `METHODS.toml`.
- `.mthds` files MAY be at the root or in subdirectories. A compliant implementation MUST discover all `.mthds` files recursively.
- A single directory SHOULD contain one package. Multiple packages in subdirectories with distinct addresses are possible but outside the scope of this specification.
## Manifest Discovery
When loading a `.mthds` bundle, a compliant implementation SHOULD discover the manifest by walking up from the bundle file's directory:
1. Check the current directory for `METHODS.toml`.
2. If not found, move to the parent directory.
3. Stop when `METHODS.toml` is found, a `.git` directory is encountered, or the filesystem root is reached.
4. If no manifest is found, the bundle is treated as a standalone bundle (no package).
## Complete Manifest Example
```toml
[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 "]
license = "MIT"
mthds_version = ">=1.0.0"
main_pipe = "analyze_nda"
[exports.legal]
pipes = ["classify_document"]
[exports.legal.contracts]
pipes = ["extract_clause", "analyze_nda", "compare_contracts"]
[exports.scoring]
pipes = ["compute_weighted_score"]
```
## methods.lock Format
# methods.lock Format
The `methods.lock` file records the exact resolved versions and integrity hashes for all remote dependencies, enabling reproducible builds. It is auto-generated and SHOULD be committed to version control.
## File Name and Location
The lock file MUST be named `methods.lock` and MUST be located at the root of the package directory, alongside `METHODS.toml`.
## File Encoding and Syntax
`methods.lock` MUST be a valid TOML document encoded in UTF-8.
## Structure
The lock file is a flat TOML document where each top-level table key is a package address, and the value is a table containing the locked metadata for that package.
```toml
["github.com/mthds/document-processing"]
version = "1.2.3"
hash = "sha256:a1b2c3d4e5f6..."
source = "https://github.com/mthds/document-processing"
["github.com/mthds/scoring-lib"]
version = "0.5.1"
hash = "sha256:e5f6a7b8c9d0..."
source = "https://github.com/mthds/scoring-lib"
```
Because package addresses contain dots and slashes, they MUST be quoted as TOML keys.
## Locked Package Fields
Each entry in the lock file contains:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `version` | string | Yes | The exact resolved version. MUST be valid semver. |
| `hash` | string | Yes | Integrity hash of the package contents. MUST match the pattern `sha256:[0-9a-f]{64}`. |
| `source` | string | Yes | The HTTPS URL from which the package was fetched. MUST start with `https://`. |
## Hash Computation
The integrity hash is a deterministic SHA-256 hash of the package directory contents, computed as follows:
1. Collect all regular files recursively under the package directory.
2. Exclude any path containing `.git` in its components.
3. Sort files by their POSIX-normalized relative path (for cross-platform determinism).
4. For each file in sorted order, feed into the hasher:
a. The relative path string, encoded as UTF-8.
b. The raw file bytes.
5. The resulting hash is formatted as `sha256:` followed by the 64-character lowercase hex digest.
## Which Packages Are Locked
- **Remote dependencies** (those without a `path` field in the root manifest) are locked, including all transitive remote dependencies.
- **Local path dependencies** are NOT locked. They are resolved from the filesystem at load time and are expected to change during development.
## When the Lock File Updates
The lock file is regenerated when:
- `mthds pkg lock` is run — resolves all dependencies and writes the lock file.
- `mthds pkg update` is run — re-resolves to latest compatible versions and rewrites the lock file.
- `mthds pkg add` is run — adds a new dependency and may trigger re-resolution.
## Verification
When installing from a lock file (`mthds pkg install`), a compliant implementation MUST:
1. For each entry in the lock file, locate the corresponding cached package directory.
2. Recompute the SHA-256 hash of the cached directory using the algorithm described above.
3. Compare the computed hash with the `hash` field in the lock file.
4. Reject the installation if any hash does not match (integrity failure).
## Deterministic Output
Lock file entries MUST be sorted by package address (lexicographic ascending) to produce deterministic output suitable for clean version control diffs.
An empty lock file (no remote dependencies) MAY be an empty file or absent entirely.
## Namespace Resolution Rules
# Namespace Resolution Rules
This page defines the formal rules for resolving references to concepts and pipes across bundles, domains, and packages.
## Reference Syntax Overview
All references to concepts and pipes in MTHDS follow a uniform three-tier syntax:
| Tier | Syntax | Example (concept) | Example (pipe) |
|------|--------|--------------------|----------------|
| Bare | `name` | `ContractClause` | `extract_clause` |
| Domain-qualified | `domain_path.name` | `legal.contracts.NonCompeteClause` | `legal.contracts.extract_clause` |
| Package-qualified | `alias->domain_path.name` | `acme->legal.ContractClause` | `docproc->extraction.extract_text` |
## Parsing Rules
### Splitting Cross-Package References
If the reference string contains `->`, it is a cross-package reference. The string is split on the first `->`:
- Left part: the package alias.
- Right part: the remainder (a domain-qualified or bare reference).
The alias MUST be `snake_case`. The remainder is parsed as a domain-qualified or bare reference.
### Splitting Domain-Qualified References
For the remainder (or the entire string if no `->` is present), the reference is parsed by splitting on the **last `.`** (dot):
- Left part: the domain path.
- Right part: the local code (concept code or pipe code).
If no `.` is present, the reference is a bare name with no domain qualification.
**Examples:**
| Reference | Domain Path | Local Code | Type |
|-----------|-------------|------------|------|
| `extract_clause` | *(none)* | `extract_clause` | Bare pipe |
| `NonCompeteClause` | *(none)* | `NonCompeteClause` | Bare concept |
| `scoring.compute_score` | `scoring` | `compute_score` | Domain-qualified pipe |
| `legal.contracts.NonCompeteClause` | `legal.contracts` | `NonCompeteClause` | Domain-qualified concept |
| `docproc->extraction.extract_text` | `extraction` (in package `docproc`) | `extract_text` | Package-qualified pipe |
### Disambiguation: Concept vs. Pipe
When parsing a domain-qualified reference, the casing of the local code (the segment after the last `.`) determines whether it is a concept or a pipe:
- `PascalCase` (`[A-Z][a-zA-Z0-9]*`) → concept code.
- `snake_case` (`[a-z][a-z0-9_]*`) → pipe code.
This disambiguation is unambiguous because concept codes and pipe codes follow mutually exclusive casing conventions.
## Domain Path Validation
Each segment of a domain path MUST be `snake_case`:
- Match pattern: `[a-z][a-z0-9_]*`
- Segments are separated by `.`
- No leading, trailing, or consecutive dots
## Resolution Order for Bare Concept References
When resolving a bare concept code (no domain qualifier, no package prefix):
1. **Native concepts** — check if the code matches a native concept code (`Dynamic`, `Text`, `Image`, `Document`, `Html`, `TextAndImages`, `Number`, `Page`, `JSON`, `SearchResult`, `Anything`). Native concepts always take priority.
2. **Current bundle** — check concepts declared in the same `.mthds` file.
3. **Same domain, other bundles** — if the bundle is part of a package, check concepts in other bundles that declare the same domain.
4. **Error** — if not found in any of the above, the reference is invalid.
Bare concept references do NOT fall through to other domains or other packages.
## Resolution Order for Bare Pipe References
When resolving a bare pipe code (no domain qualifier, no package prefix):
1. **Current bundle** — check pipes declared in the same `.mthds` file.
2. **Same domain, other bundles** — if the bundle is part of a package, check pipes in other bundles that declare the same domain.
3. **Error** — if not found, the reference is invalid.
Bare pipe references do NOT fall through to other domains or other packages.
## Resolution of Domain-Qualified References
When resolving `domain_path.name` (no package prefix):
1. Look in the named domain within the **current package**.
2. If not found: **error**. Domain-qualified references do not fall through to dependencies.
This applies to both concept and pipe references.
## Resolution of Package-Qualified References
When resolving `alias->domain_path.name`:
1. Identify the dependency by the alias. The alias MUST match a key in the `[dependencies]` section of the consuming package's `METHODS.toml`.
2. Look in the named domain of the **resolved dependency package**.
3. If not found: **error**.
**Visibility constraints for cross-package pipe references:**
- The referenced pipe MUST be exported by the dependency package (listed in its `[exports]` section or declared as `main_pipe` in its bundle header).
- If the pipe is not exported, the reference is a visibility error.
**Visibility for cross-package concept references:**
- Concepts are always public. No visibility check is needed for cross-package concept references.
## Visibility Rules (Intra-Package)
Within a package that has a `METHODS.toml` manifest:
- **Same-domain references** — always allowed. A pipe in domain `legal.contracts` can reference any other pipe in `legal.contracts` without restriction.
- **Cross-domain references** (within the same package) — the target pipe MUST be exported. A pipe in domain `scoring` referencing `legal.contracts.extract_clause` requires that `extract_clause` is listed in `[exports.legal.contracts]` (or is the `main_pipe` of a bundle in `legal.contracts`).
- **Bare references** — always allowed at the visibility level (they resolve within the same domain).
When no manifest is present (standalone bundle), all pipes are treated as public.
## Reserved Domains
The following domain names are reserved at the first segment level:
| Domain | Owner | Purpose |
|--------|-------|---------|
| `native` | MTHDS standard | Built-in concept types. |
| `mthds` | MTHDS standard | Reserved for future standard extensions. |
| `pipelex` | Reference implementation | Reserved for the reference implementation. |
**Enforcement points:**
- A compliant implementation MUST reject `METHODS.toml` exports that use a reserved domain path.
- A compliant implementation MUST reject bundles that declare a domain starting with a reserved segment when the bundle is part of a package.
- A compliant implementation MUST reject packages at publish time if any bundle uses a reserved domain.
The `native` domain is the only reserved domain with active semantics: it serves as the namespace for native concepts (`native.Text`, `native.Image`, etc.).
## Package Namespace Isolation
Two packages MAY declare the same domain name (e.g., both declare `domain = "recruitment"`). Their concepts and pipes are completely independent — there is no merging of namespaces across packages.
Within a single package, bundles that share the same domain DO merge their namespace. Concept or pipe code collisions within the same package and same domain are errors.
## Conflict Rules
| Scope | Conflict type | Result |
|-------|--------------|--------|
| Same bundle | Duplicate concept code | TOML parse error (duplicate key). |
| Same bundle | Duplicate pipe code | TOML parse error (duplicate key). |
| Same domain, different bundles (same package) | Duplicate concept code | Error at load time. |
| Same domain, different bundles (same package) | Duplicate pipe code | Error at load time. |
| Different domains (same package) | Same concept or pipe code | No conflict — different namespaces. |
| Different packages | Same domain and same concept/pipe code | No conflict — package isolation. |
## Version Resolution Strategy
When resolving dependency versions, a compliant implementation SHOULD use **Minimum Version Selection** (MVS), following Go's approach:
1. Collect all version constraints for a given package address from all dependents (direct and transitive).
2. List all available versions (from VCS tags).
3. Sort versions in ascending order.
4. Select the **minimum** version that satisfies **all** constraints simultaneously.
If no version satisfies all constraints, the resolution fails with an error.
**Properties of MVS:**
- **Deterministic** — the same set of constraints always produces the same result.
- **Reproducible** — no dependency on a "latest" query or timestamp.
- **Simple** — no backtracking solver needed.
## Transitive Dependency Resolution
Dependencies are resolved transitively with the following rules:
- **Remote dependencies** are resolved recursively. If Package A depends on Package B, and Package B depends on Package C, then Package C is also resolved.
- **Local path dependencies** are resolved at the root level only. They are NOT resolved transitively.
- **Cycle detection** — if a dependency is encountered while it is already on the resolution stack, the resolver MUST report a cycle error.
- **Diamond dependencies** — when the same package address is required by multiple dependents with different version constraints, MVS selects the minimum version satisfying all constraints simultaneously.
## Fetching Remote Dependencies
Package addresses map to Git clone URLs by the following rule:
1. Prepend `https://`.
2. Append `.git` (if not already present).
For example: `github.com/acme/legal-tools` → `https://github.com/acme/legal-tools.git`
The resolution chain for fetching a dependency is:
1. **Local path** — if the dependency has a `path` field in `METHODS.toml`, resolve from the local filesystem.
2. **Local cache** — check `~/.mthds/packages/{address}/{version}/` for a cached copy.
3. **VCS fetch** — clone the repository at the resolved version tag using `git clone --depth 1 --branch {tag}`.
Version tags in the remote repository MAY use a `v` prefix (e.g., `v1.0.0`). The prefix is stripped during version parsing.
## Cache Layout
The default package cache is located at `~/.mthds/packages/`. Cached packages are stored at:
```
~/.mthds/packages/{address}/{version}/
```
For example:
```
~/.mthds/packages/github.com/acme/legal-tools/1.0.0/
```
The `.git` directory is removed from cached copies.
## Cross-Package Reference Examples
The following examples illustrate the complete reference resolution for cross-package scenarios.
**Setup:** Package A depends on Package B with alias `scoring_lib`.
Package B (`METHODS.toml`):
```toml
[package]
address = "github.com/mthds/scoring-lib"
version = "0.5.0"
description = "Scoring utilities"
[exports.scoring]
pipes = ["compute_weighted_score"]
```
Package B (`scoring.mthds`):
```toml
domain = "scoring"
main_pipe = "compute_weighted_score"
[concept.ScoreResult]
description = "A weighted score result"
[pipe.compute_weighted_score]
type = "PipeLLM"
description = "Compute a weighted score"
inputs = { item = "Text" }
output = "ScoreResult"
prompt = "Compute a weighted score for: $item"
[pipe.internal_helper]
type = "PipeLLM"
description = "Internal helper (not exported)"
inputs = { data = "Text" }
output = "Text"
prompt = "Process: $data"
```
Package A (`analysis.mthds`):
```toml
domain = "analysis"
[pipe.analyze_item]
type = "PipeSequence"
description = "Analyze using scoring dependency"
inputs = { item = "Text" }
output = "Text"
steps = [
{ pipe = "scoring_lib->scoring.compute_weighted_score", result = "score" },
{ pipe = "summarize", result = "summary" },
]
```
**Resolution of `scoring_lib->scoring.compute_weighted_score`:**
1. `->` detected — split into alias `scoring_lib` and remainder `scoring.compute_weighted_score`.
2. Look up `scoring_lib` in Package A's `[dependencies]` — found, resolves to `github.com/mthds/scoring-lib`.
3. Parse remainder: split on last `.` → domain `scoring`, pipe code `compute_weighted_score`.
4. Look in domain `scoring` of the resolved Package B — pipe found.
5. Visibility check: `compute_weighted_score` is in `[exports.scoring]` pipes — accessible.
6. Resolution succeeds.
**If Package A tried `scoring_lib->scoring.internal_helper`:**
1. Steps 1–4 as above — pipe `internal_helper` is found in Package B's `scoring` domain.
2. Visibility check: `internal_helper` is NOT in `[exports.scoring]` and is NOT `main_pipe` — **visibility error**.
**Cross-package concept reference:**
```toml
[concept.DetailedScore]
description = "An extended score with additional analysis"
refines = "scoring_lib->scoring.ScoreResult"
```
This refines `ScoreResult` from Package B. Concepts are always public, so no visibility check is needed.
## Validation Rule Summary
This section consolidates the validation rules scattered throughout this specification into a single reference.
### Bundle-Level Validation
1. The file MUST be valid TOML.
2. `domain` MUST be present and MUST be a valid domain code.
3. `main_pipe`, if present, MUST be `snake_case` and MUST reference a pipe defined in the same bundle.
4. Concept codes MUST be `PascalCase`.
5. Concept codes MUST NOT match any native concept code.
6. Pipe codes MUST be `snake_case`.
7. `refines` and `structure` MUST NOT both be set on the same concept.
8. Local concept references (bare or same-domain) MUST resolve to a declared concept in the bundle or a native concept.
9. Same-domain pipe references MUST resolve to a declared pipe in the bundle.
10. Cross-package references (`->` syntax) are deferred to package-level validation.
### Concept Structure Field Validation
1. `description` MUST be present on every field.
2. If `type` is omitted, `choices` MUST be non-empty.
3. `type = "dict"` requires both `key_type` and `value_type`.
4. `type = "concept"` requires `concept_ref` and forbids `default_value`.
5. `type = "list"` with `item_type = "concept"` requires `item_concept_ref`.
6. `concept_ref` MUST NOT be set unless `type = "concept"`.
7. `item_concept_ref` MUST NOT be set unless `item_type = "concept"`.
8. `default_value` type MUST match the declared `type`.
9. If `choices` is set and `default_value` is present, `default_value` MUST be in `choices`.
10. Field names MUST NOT start with `_`.
### Pipe Validation (Type-Specific)
1. **PipeLLM**: All prompt variables MUST have matching inputs. All inputs MUST be used.
2. **PipeStructure**: Exactly one input MUST be declared. The input concept MUST be `Text` or refine `Text`. `output` MUST NOT be `Text` or refine `Text`.
3. **PipeFunc**: `function_name` MUST be present.
4. **PipeImgGen**: `prompt` MUST be present. All prompt variables MUST have matching inputs.
5. **PipeExtract**: Exactly one input MUST be declared. `output` MUST be `"Page[]"`.
6. **PipeCompose**: Exactly one of `template` or `construct` MUST be present. Output MUST NOT use multiplicity.
7. **PipeSequence**: `steps` MUST have at least one entry.
8. **PipeParallel**: At least one of `add_each_output` or `combined_output` MUST be set.
9. **PipeCondition**: Exactly one of `expression_template` or `expression` MUST be present. `outcomes` MUST have at least one entry.
10. **PipeBatch**: `input_list_name` MUST be in `inputs`. `input_item_name` MUST NOT equal `input_list_name` or any `inputs` key.
### Package-Level Validation
1. `[package]` section MUST be present in `METHODS.toml`.
2. `address` MUST match the hostname/path pattern.
3. `version` MUST be valid semver.
4. `description` MUST NOT be empty.
5. All dependency aliases MUST be unique.
6. All dependency aliases MUST be `snake_case`.
7. All dependency addresses MUST match the hostname/path pattern.
8. All dependency version constraints MUST be valid.
9. Domain paths in `[exports]` MUST NOT use reserved domains.
10. All pipe codes in `[exports]` MUST be valid `snake_case`.
11. Cross-package references MUST reference known dependency aliases.
12. Cross-package pipe references MUST target exported pipes.
13. Bundles MUST NOT use reserved domains as their first segment.
### Lock File Validation
1. Each entry's `version` MUST be valid semver.
2. Each entry's `hash` MUST match `sha256:[0-9a-f]{64}`.
3. Each entry's `source` MUST start with `https://`.
## Summary: Reference Resolution Flowchart
Given a reference string `R`:
```
1. Does R contain "->"?
YES → Split into (alias, remainder).
Look up alias in [dependencies].
Parse remainder as domain-qualified or bare ref.
Resolve in the dependency's namespace.
For pipes: check export visibility.
NO → Continue to step 2.
2. Does R contain "."?
YES → Split on last "." into (domain_path, local_code).
Resolve in domain_path within current package.
NO → R is a bare name. Continue to step 3.
3. Is R a concept code (PascalCase)?
YES → Check native concepts → current bundle → same domain.
NO → R is a pipe code (snake_case).
Check current bundle → same domain.
4. Not found? → Error.
```
## CLI I/O Contract
# CLI I/O Contract
This page defines the input/output contract for MTHDS methods when invoked as CLI commands. It specifies what a method writes to stdout, what it reads from stdin, and how errors propagate through pipe chains.
## Output Modes
A method's CLI produces structured JSON on stdout. Two output modes are defined: **compact** (default) and **full** (opt-in via `--with-memory`).
### Compact Output (Default)
The concept's rendered JSON is emitted directly — no envelope, no metadata:
```json
{
"clauses": [
{ "title": "Non-Compete", "risk_level": "high" },
{ "title": "Termination", "risk_level": "medium" }
],
"overall_risk": "high"
}
```
This is the structured content of the method's main output concept. Standard JSON tools work directly:
```bash
mthds-agent pipelex run method extract-terms | jq '.clauses[] | select(.risk_level == "high")'
# Or using the installed CLI shim:
extract-terms | jq '.clauses[] | select(.risk_level == "high")'
```
When the method has no main output (empty working memory), an empty object `{}` is emitted.
### Full Output (`--with-memory`)
When `--with-memory` is passed, the output includes the main stuff renderings and the full working memory:
```json
{
"main_stuff": {
"json": "",
"markdown": "",
"html": ""
},
"working_memory": {
"root": {
"contract_text": {
"stuff_code": "...",
"stuff_name": "contract_text",
"concept": { "code": "Text" },
"content": { "text": "The parties agree..." }
},
"extracted_terms": {
"stuff_code": "...",
"stuff_name": "extracted_terms",
"concept": { "code": "ContractAnalysis" },
"content": { "clauses": ["..."], "overall_risk": "high" }
},
"main_stuff": {
"stuff_code": "...",
"stuff_name": null,
"concept": { "code": "ContractAnalysis" },
"content": { "clauses": ["..."], "overall_risk": "high" }
}
},
"aliases": {
"main_stuff": "extracted_terms"
}
}
}
```
The full output preserves all intermediate results and aliases from the pipeline's working memory. This is required when piping output to another method, because the downstream method may need intermediate stuffs for multi-input binding.
### Side Effects
Regardless of output mode, the runtime may write side-effect files to disk:
- **Output JSON** (`live_run.json` / `dry_run.json`) — the full execution result saved alongside the bundle.
- **Graph HTML** (`live_run.html` / `dry_run.html`) — execution graph visualizations (generated by default, disabled with `--no-graph`).
These files are not included in stdout output. Their paths appear in runtime logs on stderr.
## Input Acceptance
A method accepts inputs through three sources, resolved in priority order:
### 1. `--inputs` Flag (Highest Priority)
The `--inputs` / `-i` flag accepts a file path or inline JSON string. If the value starts with `{`, it is parsed as inline JSON; otherwise it is treated as a file path.
```bash
# Inline JSON
mthds-agent pipelex run method my_method --inputs '{"text": {"concept": "native.Text", "content": {"text": "hello"}}}'
# File path
mthds-agent pipelex run method my_method --inputs data.json
```
When a method is installed as a CLI shim (see [CLI Reference](../cli/index.md)), the same commands are available as:
```bash
my_method --inputs '{"text": {"concept": "native.Text", "content": {"text": "hello"}}}'
my_method --inputs data.json
```
When `--inputs` is provided, stdin is ignored entirely. This allows overriding piped data for debugging.
### 2. stdin (Fallback)
When `--inputs` is not provided and stdin is not a TTY (i.e., data is being piped), JSON is read from stdin:
```bash
echo '{"text": {"concept": "native.Text", "content": {"text": "hello"}}}' | mthds-agent pipelex run method my_method
```
When stdin is a TTY (interactive terminal), no stdin reading occurs and the method runs without inputs.
### 3. Auto-detected `inputs.json`
In directory mode, `inputs.json` in the target directory is auto-detected and used as a fallback when no explicit inputs are provided.
## Envelope Detection
When JSON arrives via stdin, the runtime distinguishes between two formats based on the presence of a `working_memory` key at the top level:
### Flat Inputs
No `working_memory` key present. The JSON is treated as direct input bindings — the same format as `--inputs`:
```json
{
"document": {
"concept": "native.Document",
"content": { "url": "/path/to/file.pdf" }
}
}
```
### Full Envelope
A `working_memory` key is present. This indicates the JSON came from an upstream method's `--with-memory` output. The runtime extracts named stuffs from `working_memory.root` and converts them to input bindings.
The input resolution rules when receiving a full envelope:
1. **Name matching**: Each stuff name in the upstream working memory's `root` is matched against the downstream method's declared input names. Matching entries are bound automatically.
2. **Single-input shortcut**: If the downstream method declares exactly one input, it auto-binds to the upstream's `main_stuff` content. This is the common case for simple chains:
```bash
extract-terms --with-memory | assess-risk
```
3. **Error on failure**: If the downstream method's declared inputs cannot be satisfied from the upstream's working memory, a clear error is raised listing what was available upstream vs. what was expected downstream.
## Error Propagation
Errors are emitted as structured JSON on **stderr** with a non-zero exit code:
```json
{
"error": true,
"error_type": "PipelineExecutionError",
"message": "Pipe 'assess_risk' failed: missing required input 'analysis'",
"hint": "Check 'pipe_stack' to identify which pipe failed",
"error_domain": "runtime",
"retryable": false
}
```
In a Unix pipe chain, errors stop execution at the failing step. Use `set -o pipefail` in shell scripts to ensure mid-chain failures propagate:
```bash
set -o pipefail
extract-terms --with-memory \
| assess-risk --with-memory \
| generate-report
```
## Examples
### Simple Chain (Single Input)
```bash
# Extract terms, then assess risk, then generate report
extract-terms --inputs '{"document": {"concept": "native.Document", "content": {"url": "contract.pdf"}}}' --with-memory \
| assess-risk --with-memory \
| generate-report
```
Each intermediate step uses `--with-memory` to pass the full envelope. The final step omits it to produce compact output.
### Compact Output with jq
```bash
# Extract just the high-risk clauses
extract-terms --inputs data.json \
| jq '.clauses[] | select(.risk_level == "high")'
```
### Override Piped Input
```bash
# The --inputs flag overrides whatever comes from stdin
echo '{"old": "data"}' | mthds-agent pipelex run method my_method --inputs '{"new": "data"}'
```
The `--inputs` flag always wins — the stdin data is ignored.
## HTTP Runner Protocol
# HTTP Runner Protocol
The MTHDS Protocol is the minimal HTTP contract every MTHDS runner implements. Any server that serves these five routes with the shapes defined here is an MTHDS-compliant runner. A runner is just a runner: it executes methods, validates bundles, and reports what models it can route to and what version it is. It keeps no run store and owns no user, billing, or catalog concepts.
The normative artifact is the OpenAPI document: [`mthds-protocol.openapi.yaml`](openapi/mthds-protocol.openapi.yaml). This page walks through it in prose, then renders it route by route — every parameter, request body, and response schema — in the [route reference](#route-reference) below. Where prose and YAML disagree, the YAML wins.
## The five routes
| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/execute` | Execute a method synchronously; the full output comes back in the response. |
| `POST` | `/start` | Start a method asynchronously; returns its `pipeline_run_id` immediately (202). Completion delivery is implementation-defined. |
| `POST` | `/validate` | Parse, validate, and dry-run an MTHDS bundle. |
| `GET` | `/models` | The models this runner can route to. Optional `?type=` filter (`llm` · `extract` · `img_gen` · `search`). |
| `GET` | `/version` | Always public. Protocol and runner versions — the handshake clients use for feature detection. |
All errors are [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) `application/problem+json` documents. Auth is implementation-defined: a bearer-token slot is reserved, and anonymous access is allowed for self-hosted runners.
## Base URL and versioning
Protocol paths are version-agnostic. The version segment belongs to the server base URL, chosen by the implementation:
```
http://localhost:8081/v1/execute
https://api.example.com/v1/execute
```
The protocol itself is versioned by this standard (`protocol_version` in `/version`); each implementation versions its own mount point. A client written against the protocol composes `{base_url}/{path}` and never inspects the base URL's structure.
## Executing a method
`POST /execute` is blocking: the response carries the method's full output. The request body is a `RunRequest`:
```json
{
"pipe_code": "analyze_contract",
"mthds_contents": ["domain = \"legal\"\n..."],
"inputs": {
"contract": { "concept": "Document", "content": { "file_path": "..." } }
}
}
```
At least one of `pipe_code` / `mthds_contents` is required. If `mthds_contents` is provided without `pipe_code`, the first bundle must declare a `main_pipe`. Optional fields: `output_name`, `output_multiplicity`, `dynamic_output_concept_ref`.
The 200 response is a `RunResultExecute` — the completed run, holding two base fields: `pipeline_run_id` (mandatory, server-generated and authoritative) and `pipe_output` (the method's serialized output; always present — a completed run has output). Anything more an implementation returns — a run state, timestamps, output naming, anything else — is an extension field (see [Extension policy](#extension-policy)), declared and documented by that implementation.
The protocol sets no time limit on `/execute`; deployments cap it at their proxy layer. For long-running methods prefer `/start`. Implementations **MAY** answer `202 + RunResultStart` (just the run id, no output yet) with a `Location` header pointing at an implementation-defined status resource when they cannot hold the connection open ([RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.3) asynchronous pattern). Simple runners never emit 202; clients that cannot handle it should use `/start`.
## Starting a method asynchronously
`POST /start` accepts the same `RunRequest` body as `/execute` — the protocol declares no start-only request fields. Anything an implementation accepts on top (a client-supplied run identifier, anything else) is an extension arg (see [Extension policy](#extension-policy)), defined and documented by that implementation.
The response is `202 + RunResultStart` — just the authoritative server-generated `pipeline_run_id` (plus any implementation extension fields). A started run has no output yet.
### No run store, no completion channel
The protocol mandates no run store, and it defines **no completion channel for `/start`**: how a caller learns that an asynchronous run finished — webhooks, polling routes, anything else — is implementation-defined and outside the protocol (see [Extension policy](#extension-policy)). A bare runner is not required to answer "what happened to run X?" after the fact. Clients written against the protocol alone must rely on `/execute`'s response.
## Validating a bundle
`POST /validate` takes `mthds_contents` (always an array, even for a single file) and an optional `allow_signatures` flag (default `false` — strict; when `true`, the validation sweep tolerates unimplemented pipe signatures by minting a mock).
`/validate` is a **diagnostic endpoint**: its job is to return a *verdict* about the submitted bundle, and every verdict it can produce — valid or invalid — rides a **200**, discriminated in the body on the mandatory `is_valid` field.
- **`is_valid: true`** — the bundle is valid. The protocol declares the `is_valid` discriminant plus the runnability facts (`is_runnable`, and `pending_signatures` — refs of pipes still declared as unimplemented signatures); implementations MAY include their own artifacts (parsed structures, graphs, anything else) as additional properties.
- **`is_valid: false`** — the bundle is invalid. The body carries `validation_errors[]` (a non-empty list of structured diagnostics, each at least a `category` and a `message`) plus `is_runnable: false` and an optional `message`. The structural artifacts of a valid report are absent.
A client pattern-matches `is_valid` to learn the verdict — it never inspects a status code or catches an exception body. Non-2xx is reserved for the cases where **no verdict could be produced**: a malformed request body is a `422` problem, auth a `401`/`403`, a server fault a `5xx`. So a non-2xx on `/validate` always means "the endpoint could not produce a verdict," never "your bundle is bad" — which keeps expected validation failures out of the 4xx error budget and never editorializes a verdict into a spurious retry. Signatures are never an error: an unimplemented signature reached during validation is a *runnability fact* (`is_runnable: false` + `pending_signatures`), not a validation failure, and `allow_signatures` only affects the dry-run sweep, not the verdict.
## Discovery
`GET /models` returns the runner's model deck — the models it can route to (`{name, type}` entries), optionally filtered by category. Implementations may add their own routing metadata (aliases, fallback chains, anything else) as additional properties.
`GET /version` is always public (no auth). It returns `protocol_version` (required) and an optional `runner_version` — implementations may add their own identification on top:
```json
{
"protocol_version": "0.6.0",
"runner_version": "2.3.0"
}
```
Clients use `/version` as the handshake: it reports the protocol and runner versions, and any additional properties let clients detect vendor extensions before relying on them.
## Extension policy
Implementations may extend the surface — extra routes, extra optional request properties, extra response properties — but **must not change the meaning or shape of the protocol routes**. Both sides of the wire are extension-open: request bodies accept implementation-defined args, and the protocol's response schemas declare only the base fields (`additionalProperties` allowed). A client written against the MTHDS Protocol runs unmodified against any compliant runner; a vendor's superset may accept and return more, but never diverges on the surface defined here.
## Conformance
An implementation claiming conformance states it as: *implements MTHDS Protocol v0.1*. Conformance means: the five routes exist with the request/response shapes of [`mthds-protocol.openapi.yaml`](openapi/mthds-protocol.openapi.yaml), errors are RFC 7807 problems, and `/version` is public.
## Route reference
Everything below is rendered at build time from the normative OpenAPI document — do not edit it by hand; edit [`mthds-protocol.openapi.yaml`](openapi/mthds-protocol.openapi.yaml).
## Servers
Description
URL
Example — a self-hosted runner. The version segment belongs to the server base URL; protocol paths are version-agnostic.
http://localhost:8081/v1
Example — a hosted, protocol-compliant superset.
https://api.example.com/v1
## run
### POST /execute
Execute a method synchronously and return its full output.
??? note "Description"
Blocking. The protocol sets no time limit; deployments cap it at their proxy layer. For long-running methods prefer /start. Implementations MAY return 202 + RunResultStart (id only) with a Location header pointing at an implementation-defined status resource when they cannot hold the connection open for the full execution (RFC 9110 asynchronous pattern); clients that cannot handle 202 should use /start instead.
**Input parameters**
Parameter
In
Type
Default
Nullable
Description
bearer
header
string
N/A
No
Token semantics are implementation-defined.
Request body
=== "application/json"
```json
{
"pipe_code": "string",
"mthds_contents": [
"string"
],
"inputs": {},
"output_name": "string",
"output_multiplicity": null,
"dynamic_output_concept_ref": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the request body"
```json
{
"type": "object",
"description": "At least one of pipe_code / mthds_contents is required (enforced by the anyOf rule below). If mthds_contents is provided without pipe_code, the first bundle must declare a main_pipe. Extension-open: implementations MAY accept extra top-level properties (extension args).\n",
"additionalProperties": true,
"anyOf": [
{
"required": [
"pipe_code"
],
"properties": {
"pipe_code": {
"type": "string",
"minLength": 1
}
}
},
{
"required": [
"mthds_contents"
],
"properties": {
"mthds_contents": {
"type": "array",
"minItems": 1
}
}
}
],
"properties": {
"pipe_code": {
"type": [
"string",
"null"
],
"description": "Code of the pipe to execute (a pipe already registered, or one defined in mthds_contents)."
},
"mthds_contents": {
"type": [
"array",
"null"
],
"minItems": 1,
"items": {
"type": "string"
},
"description": "MTHDS bundle contents to load (always an array, even for a single file; never empty). Implementations bound count and per-file size."
},
"inputs": {
"type": [
"object",
"null"
],
"description": "Method inputs: map of input name to { concept, content }. Content shapes follow the concept's structure; content validation is deliberately loose here and strict inside the runtime.",
"additionalProperties": {
"type": "object",
"required": [
"concept",
"content"
],
"properties": {
"concept": {
"type": "string"
},
"content": {}
}
}
},
"output_name": {
"type": [
"string",
"null"
],
"description": "Name of the output slot to return as the main output."
},
"output_multiplicity": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"description": "Output multiplicity override (false/true or an explicit count)."
},
"dynamic_output_concept_ref": {
"type": [
"string",
"null"
],
"description": "Override for the dynamic output concept reference."
}
}
}
```
Responses
=== "200 OK"
=== "application/json"
```json
{
"pipeline_run_id": "string",
"pipe_output": {}
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "POST /execute 200 — the completed run. Two base fields: the server-generated authoritative pipeline_run_id and the method's pipe_output (always present — a completed run has output). Extension-open: anything more an implementation returns (a run state, timestamps, output naming) is an extension on top.\n",
"additionalProperties": true,
"required": [
"pipeline_run_id",
"pipe_output"
],
"properties": {
"pipeline_run_id": {
"type": "string",
"description": "The run identifier — server-generated and authoritative."
},
"pipe_output": {
"description": "The method's serialized output (working memory of serialized stuffs).",
"type": "object"
}
}
}
```
=== "202 Accepted"
=== "application/json"
```json
{
"pipeline_run_id": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "POST /start 202 (and the optional /execute 202 degrade) — the started run's authoritative pipeline_run_id, nothing else. A started run has no output yet; how it is delivered later (polling, callbacks, anything else) is implementation-defined and outside the protocol. Extension-open.\n",
"additionalProperties": true,
"required": [
"pipeline_run_id"
],
"properties": {
"pipeline_run_id": {
"type": "string",
"description": "The run identifier — server-generated and authoritative."
}
}
}
```
**Response headers**
| Name | Description | Schema |
| --- | --- | --- |
| `Location` | Implementation-defined status resource for this run. |string |
=== "422 Unprocessable Content"
Refer to the common response description: ValidationProblem.
=== "Other responses"
Refer to the common response description: Problem.
### POST /start
Start a method asynchronously; returns its pipeline_run_id immediately.
??? note "Description"
Asynchronous. Returns 202 + RunResultStart immediately (pipeline_run_id only); the runner keeps no run store, and how completion is later delivered (callbacks, polling, anything else) is implementation-defined and outside the protocol. The returned pipeline_run_id is always authoritative (server-generated).
**Input parameters**
Parameter
In
Type
Default
Nullable
Description
bearer
header
string
N/A
No
Token semantics are implementation-defined.
Request body
=== "application/json"
```json
{
"pipe_code": "string",
"mthds_contents": [
"string"
],
"inputs": {},
"output_name": "string",
"output_multiplicity": null,
"dynamic_output_concept_ref": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the request body"
```json
{
"type": "object",
"description": "At least one of pipe_code / mthds_contents is required (enforced by the anyOf rule below). If mthds_contents is provided without pipe_code, the first bundle must declare a main_pipe. Extension-open: implementations MAY accept extra top-level properties (extension args).\n",
"additionalProperties": true,
"anyOf": [
{
"required": [
"pipe_code"
],
"properties": {
"pipe_code": {
"type": "string",
"minLength": 1
}
}
},
{
"required": [
"mthds_contents"
],
"properties": {
"mthds_contents": {
"type": "array",
"minItems": 1
}
}
}
],
"properties": {
"pipe_code": {
"type": [
"string",
"null"
],
"description": "Code of the pipe to execute (a pipe already registered, or one defined in mthds_contents)."
},
"mthds_contents": {
"type": [
"array",
"null"
],
"minItems": 1,
"items": {
"type": "string"
},
"description": "MTHDS bundle contents to load (always an array, even for a single file; never empty). Implementations bound count and per-file size."
},
"inputs": {
"type": [
"object",
"null"
],
"description": "Method inputs: map of input name to { concept, content }. Content shapes follow the concept's structure; content validation is deliberately loose here and strict inside the runtime.",
"additionalProperties": {
"type": "object",
"required": [
"concept",
"content"
],
"properties": {
"concept": {
"type": "string"
},
"content": {}
}
}
},
"output_name": {
"type": [
"string",
"null"
],
"description": "Name of the output slot to return as the main output."
},
"output_multiplicity": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"description": "Output multiplicity override (false/true or an explicit count)."
},
"dynamic_output_concept_ref": {
"type": [
"string",
"null"
],
"description": "Override for the dynamic output concept reference."
}
}
}
```
Responses
=== "202 Accepted"
=== "application/json"
```json
{
"pipeline_run_id": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "POST /start 202 (and the optional /execute 202 degrade) — the started run's authoritative pipeline_run_id, nothing else. A started run has no output yet; how it is delivered later (polling, callbacks, anything else) is implementation-defined and outside the protocol. Extension-open.\n",
"additionalProperties": true,
"required": [
"pipeline_run_id"
],
"properties": {
"pipeline_run_id": {
"type": "string",
"description": "The run identifier — server-generated and authoritative."
}
}
}
```
=== "422 Unprocessable Content"
Refer to the common response description: ValidationProblem.
=== "Other responses"
Refer to the common response description: Problem.
## validate
### POST /validate
Parse, validate, and dry-run an MTHDS bundle.
??? note "Description"
Diagnostic endpoint. Every verdict the validator can produce — valid or invalid — rides a 200, discriminated in the body on is_valid. Non-2xx is reserved for the cases where no verdict could be produced (a malformed request body, auth, a server fault), so a 422 here is a request-shape problem, never "the bundle is invalid".
**Input parameters**
Parameter
In
Type
Default
Nullable
Description
bearer
header
string
N/A
No
Token semantics are implementation-defined.
Request body
=== "application/json"
```json
{
"mthds_contents": [
"string"
],
"allow_signatures": true
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the request body"
```json
{
"type": "object",
"required": [
"mthds_contents"
],
"properties": {
"mthds_contents": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
},
"description": "MTHDS contents to load (always an array, even for a single file)."
},
"allow_signatures": {
"type": "boolean",
"default": false,
"description": "When true, the validation sweep tolerates unimplemented pipe signatures (signatures dry-run by minting a mock). Strict by default."
}
}
}
```
Responses
=== "200 OK"
=== "application/json"
??? hint "Schema of the response body"
```json
{
"description": "The 200 response of POST /validate — a produced verdict discriminated on the mandatory is_valid field. A client pattern-matches is_valid to learn the verdict; it never inspects a status code or catches an exception body. Non-2xx is a no-verdict condition (a request-shape problem, auth, a server fault), never an invalid bundle.\n",
"oneOf": [
{
"$ref": "#/components/schemas/ValidationReport"
},
{
"$ref": "#/components/schemas/InvalidValidationReport"
}
]
}
```
=== "422 Unprocessable Content"
Refer to the common response description: ValidationProblem.
=== "Other responses"
Refer to the common response description: Problem.
## discovery
### GET /models
The model deck available on this runner.
**Input parameters**
Parameter
In
Type
Default
Nullable
Description
bearer
header
string
N/A
No
Token semantics are implementation-defined.
type
query
string
No
Filter the deck by model category.
Responses
=== "200 OK"
=== "application/json"
```json
{
"models": [
{
"name": "string",
"type": "llm"
}
]
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "The models this runner can route to. Implementations MAY add their own routing metadata (aliases, fallback chains, anything else) as additional properties — on the deck and on each model entry.\n",
"additionalProperties": true,
"properties": {
"models": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"llm",
"extract",
"img_gen",
"search"
]
}
}
}
}
}
}
```
=== "Other responses"
Refer to the common response description: Problem.
### GET /version
Protocol and runner versions.Responses
=== "200 OK"
=== "application/json"
```json
{
"protocol_version": "string",
"runner_version": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "The handshake. The protocol defines protocol_version (required) plus an optional runner_version; implementations MAY add their own identification (a name, an underlying runtime version, anything else) as additional properties.\n",
"required": [
"protocol_version"
],
"additionalProperties": true,
"properties": {
"protocol_version": {
"type": "string",
"description": "MTHDS Protocol version implemented."
},
"runner_version": {
"type": [
"string",
"null"
],
"description": "Version of the runner serving this protocol (optional)."
}
}
}
```
=== "Other responses"
Refer to the common response description: Problem.
---
## Schemas
### InvalidValidationReport
Name
Type
Description
is_runnable
boolean
An invalid bundle is never runnable.
is_valid
boolean
Discriminant — the bundle is invalid.
message
string
Human-readable summary of the verdict.
pending_signatures
Array<string>
Outstanding signatures (best-effort; empty when no library could be assembled).
validation_errors
Array<ValidationError>
Per-error diagnostics — non-empty on every invalid verdict.
### ModelDeck
Name
Type
Description
models
Array<Properties: name, type>
### Problem
Name
Type
Description
detail
string
instance
string
status
integer
title
string
type
string(uri)
### RunRequest
Name
Type
Description
dynamic_output_concept_ref
string | null
Override for the dynamic output concept reference.
inputs
Method inputs: map of input name to { concept, content }. Content shapes follow the concept's structure; content validation is deliberately loose here and strict inside the runtime.
mthds_contents
Array<string>
MTHDS bundle contents to load (always an array, even for a single file; never empty). Implementations bound count and per-file size.
output_multiplicity
Output multiplicity override (false/true or an explicit count).
output_name
string | null
Name of the output slot to return as the main output.
pipe_code
string | null
Code of the pipe to execute (a pipe already registered, or one defined in mthds_contents).
### RunResultExecute
Name
Type
Description
pipe_output
The method's serialized output (working memory of serialized stuffs).
pipeline_run_id
string
The run identifier — server-generated and authoritative.
### RunResultStart
Name
Type
Description
pipeline_run_id
string
The run identifier — server-generated and authoritative.
### ValidateRequest
Name
Type
Description
allow_signatures
boolean
When true, the validation sweep tolerates unimplemented pipe signatures (signatures dry-run by minting a mock). Strict by default.
mthds_contents
Array<string>
MTHDS contents to load (always an array, even for a single file).
### ValidationError
Name
Type
Description
category
string
Implementation-defined diagnostic category.
message
string
### ValidationReport
Name
Type
Description
is_runnable
boolean
Whether the validated library is complete enough to run — false when pipe signatures remain unimplemented (a runnability fact, not an error).
is_valid
boolean
Discriminant — the bundle is valid.
pending_signatures
Array<string>
Refs of pipes still declared as unimplemented signatures.
### ValidationResult
Type:
### VersionInfo
Name
Type
Description
protocol_version
string
MTHDS Protocol version implemented.
runner_version
string | null
Version of the runner serving this protocol (optional).
## Common responses
This section describes common responses that are reused across operations.
### Problem
RFC 7807 problem document.
=== "application/problem+json"
```json
{
"type": "string",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "RFC 7807.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
},
"instance": {
"type": "string"
}
}
}
```
### ValidationProblem
The request failed validation (RFC 7807). On /execute and /start a bad bundle also lands here; on /validate an invalid bundle is a 200 verdict, so there a 422 is request-shape only.
=== "application/problem+json"
```json
{
"type": "string",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string"
}
```
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.
??? hint "Schema of the response body"
```json
{
"type": "object",
"description": "RFC 7807.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
},
"instance": {
"type": "string"
}
}
}
```
## Security schemes
Name
Type
Scheme
Description
bearer
http
bearer
Token semantics are implementation-defined.
## Tags
| Name | Description |
| --------- | ------------------------------------------------- |
| run | Execute methods, synchronously or asynchronously. |
| validate | Static + dry-run validation of MTHDS bundles. |
| discovery | What this runner is and what it can route to. |
## CLI Reference
# CLI Reference
The `mthds` CLI is the official command-line tool for working with MTHDS packages. It covers validation, execution, and the full package management lifecycle.
## Core Commands
### `mthds validate`
Validate `.mthds` files, individual pipes, or an entire project.
**Usage:**
```
mthds validate
mthds validate --bundle
mthds validate --bundle --pipe
mthds validate --all
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `target` | A pipe code or a bundle file path (`.mthds`). Auto-detected based on file extension. |
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--pipe` | | Pipe code to validate. Optional when using `--bundle`. |
| `--bundle` | | Bundle file path (`.mthds`). Validates all pipes in the bundle. |
| `--all` | `-a` | Validate all pipes in all loaded libraries. |
| `--library-dir` | `-L` | Directory to search for `.mthds` files. Can be specified multiple times. |
**Examples:**
```bash
# Validate a single pipe by code
mthds validate extract_clause
# Validate a bundle file
mthds validate contract_analysis.mthds
# Validate a specific pipe within a bundle
mthds validate --bundle contract_analysis.mthds --pipe extract_clause
# Validate all pipes in the project
mthds validate --all
```
---
### `mthds run`
Execute a method. Loads the bundle, resolves dependencies, and runs the specified pipe.
**Usage:**
```
mthds run
mthds run --bundle
mthds run --bundle --pipe
mthds run
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `target` | A pipe code, a bundle file path (`.mthds`), or a pipeline directory. Auto-detected. |
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--pipe` | | Pipe code to run. If omitted when using `--bundle`, runs the bundle's `main_pipe`. |
| `--bundle` | | Bundle file path (`.mthds`). |
| `--inputs` | `-i` | Path to a JSON file with input data. |
| `--output-dir` | `-o` | Base directory for all outputs. Default: `results`. |
| `--dry-run` | | Run in dry mode (no actual inference calls). |
| `--library-dir` | `-L` | Directory to search for `.mthds` files. Can be specified multiple times. |
**Examples:**
```bash
# Run a bundle's main pipe
mthds run joke_generation.mthds
# Run a specific pipe within a bundle
mthds run --bundle contract_analysis.mthds --pipe extract_clause
# Run with input data
mthds run extract_clause --inputs data.json
# Run a pipeline directory (auto-detects bundle and inputs)
mthds run pipeline_01/
# Dry run (no inference calls)
mthds run joke_generation.mthds --dry-run
```
When a directory is provided as the target, `mthds run` auto-detects the `.mthds` bundle file and an optional `inputs.json` file within it.
---
## Package Commands (`mthds pkg`)
Package commands manage the full lifecycle of MTHDS packages: initialization, dependencies, distribution, and discovery.
### `mthds pkg init`
Initialize a `METHODS.toml` package manifest from `.mthds` files in the current directory.
**Usage:**
```
mthds pkg init [--force]
```
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--force` | `-f` | Overwrite an existing `METHODS.toml`. |
The command scans all `.mthds` files recursively, extracts domain and pipe information, and generates a skeleton `METHODS.toml` with a placeholder address and auto-populated exports. Edit the generated file to set the correct address and refine exports.
**Example:**
```bash
mthds pkg init
# Created METHODS.toml with:
# Domains: 2
# Total pipes: 7
# Bundles scanned: 3
#
# Edit METHODS.toml to set the correct address and configure exports.
```
---
### `mthds pkg list`
Display the package manifest for the current directory.
**Usage:**
```
mthds pkg list
```
Walks up from the current directory to find a `METHODS.toml` and displays its contents: package identity, dependencies, and exports.
---
### `mthds pkg add`
Add a dependency to `METHODS.toml`.
**Usage:**
```
mthds pkg add [--alias NAME] [--version CONSTRAINT] [--path LOCAL_PATH]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `address` | Package address (e.g., `github.com/mthds/document-processing`). |
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--alias` | `-a` | Dependency alias. Auto-derived from the last path segment if not provided. |
| `--version` | `-v` | Version constraint. Default: `0.1.0`. |
| `--path` | `-p` | Local filesystem path to the dependency (for development). |
**Examples:**
```bash
# Add a remote dependency (alias auto-derived as "document_processing")
mthds pkg add github.com/mthds/document-processing --version "^1.0.0"
# Add with a custom alias
mthds pkg add github.com/acme/legal-tools --alias acme_legal --version "^0.3.0"
# Add a local development dependency
mthds pkg add github.com/team/scoring --path ../scoring-lib --version "^0.5.0"
```
---
### `mthds pkg lock`
Resolve dependencies and generate `methods.lock`.
**Usage:**
```
mthds pkg lock
```
Reads the `[dependencies]` section of `METHODS.toml`, resolves all versions (including transitive dependencies), and writes the lock file. The lock file records exact versions and SHA-256 integrity hashes for reproducible builds.
---
### `mthds pkg install`
Fetch and cache all dependencies from `methods.lock`.
**Usage:**
```
mthds pkg install
```
For each entry in the lock file, checks the local cache (`~/.mthds/packages/`). Missing packages are fetched via Git. After fetching, integrity hashes are verified against the lock file.
---
### `mthds pkg update`
Re-resolve dependencies to latest compatible versions and update `methods.lock`.
**Usage:**
```
mthds pkg update
```
Performs a fresh resolution of all dependencies (ignoring the existing lock file), writes the updated lock file, and displays a diff showing added, removed, and updated packages.
---
### `mthds pkg index`
Build and display the local package index.
**Usage:**
```
mthds pkg index [--cache]
```
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--cache` | `-c` | Index cached packages instead of the current project. |
Displays a summary table showing each package's address, version, description, and counts of domains, concepts, and pipes.
---
### `mthds pkg search`
Search the package index for concepts and pipes.
**Usage:**
```
mthds pkg search [options]
mthds pkg search --accepts [--produces ]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `query` | Search term (case-insensitive substring match). Optional if using `--accepts` or `--produces`. |
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--domain` | `-d` | Filter results to a specific domain. |
| `--concept` | | Show only matching concepts. |
| `--pipe` | | Show only matching pipes. |
| `--cache` | `-c` | Search cached packages instead of the current project. |
| `--accepts` | | Find pipes that accept this concept (type-compatible search). |
| `--produces` | | Find pipes that produce this concept (type-compatible search). |
**Examples:**
```bash
# Text search for concepts and pipes
mthds pkg search "contract"
# Search only pipes in a specific domain
mthds pkg search "extract" --pipe --domain legal.contracts
# Type-compatible search: "What can I do with a Document?"
mthds pkg search --accepts Document
# Type-compatible search: "What produces a NonCompeteClause?"
mthds pkg search --produces NonCompeteClause
# Combined: "What transforms Text into ScoreResult?"
mthds pkg search --accepts Text --produces ScoreResult
```
Type-compatible search uses the [Know-How Graph](../know-how-graph/index.md) to find pipes by their typed signatures. It understands concept refinement: searching for pipes that accept `Text` also finds pipes that accept `NonCompeteClause` (since `NonCompeteClause` refines `Text`).
---
### `mthds pkg inspect`
Display detailed information about a package.
**Usage:**
```
mthds pkg inspect [--cache]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `address` | Package address to inspect. |
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--cache` | `-c` | Look in the package cache instead of the current project. |
Displays the package's metadata, domains, concepts (with structure fields and refinement), and pipe signatures (with inputs, outputs, and export status).
**Example:**
```bash
mthds pkg inspect github.com/acme/legal-tools
```
---
### `mthds pkg graph`
Query the Know-How Graph for concept and pipe relationships.
**Usage:**
```
mthds pkg graph --from [--to ] [options]
mthds pkg graph --check ,
```
**Options:**
| Option | Short | Description |
|--------|-------|-------------|
| `--from` | `-f` | Concept ID — find pipes that accept it. Format: `package_address::concept_ref`. |
| `--to` | `-t` | Concept ID — find pipes that produce it. |
| `--check` | | Two pipe keys comma-separated — check if the output of the first is compatible with an input of the second. |
| `--max-depth` | `-m` | Maximum chain depth when using `--from` and `--to` together. Default: `3`. |
| `--compose` | | Show an MTHDS composition template for discovered chains. Requires both `--from` and `--to`. |
| `--cache` | `-c` | Use cached packages instead of the current project. |
**Examples:**
```bash
# Find all pipes that accept a specific concept
mthds pkg graph --from "__native__::native.Document"
# Find all pipes that produce a specific concept
mthds pkg graph --to "github.com/acme/legal-tools::legal.contracts.NonCompeteClause"
# Find chains from Document to NonCompeteClause (auto-composition)
mthds pkg graph \
--from "__native__::native.Document" \
--to "github.com/acme/legal-tools::legal.contracts.NonCompeteClause"
# Same query, but generate an MTHDS snippet for the chain
mthds pkg graph \
--from "__native__::native.Document" \
--to "github.com/acme/legal-tools::legal.contracts.NonCompeteClause" \
--compose
# Check if two pipes are compatible (can be chained)
mthds pkg graph --check "github.com/acme/legal-tools::extract_pages,github.com/acme/legal-tools::analyze_content"
```
When both `--from` and `--to` are provided, the command searches for multi-step pipe chains through the graph, up to `--max-depth` hops. With `--compose`, it generates a ready-to-use MTHDS `PipeSequence` snippet for each discovered chain.
---
## Editor Support
# Editor Support
The [Pipelex](https://marketplace.visualstudio.com/items?itemName=pipelex.pipelex) extension for VS Code-compatible editors (VS Code, Cursor, Windsurf, Antigravity, and others) provides syntax highlighting, semantic tokens, formatting, and validation for `.mthds` files. It is also available on [Open VSX](https://open-vsx.org/extension/Pipelex/pipelex). It is the recommended way to work with MTHDS.
## Installation
Install the **Pipelex** extension from the VS Code Marketplace:
1. Open your VS Code-compatible editor.
2. Go to Extensions (`Ctrl+Shift+X` / `Cmd+Shift+X`).
3. Search for **Pipelex**.
4. Click **Install**.
The extension activates automatically for `.mthds` files.
## Features
### Syntax Highlighting
The extension provides a full TextMate grammar for `.mthds` files, built on top of TOML highlighting. It recognizes MTHDS-specific constructs: pipe sections, concept sections, prompt templates, Jinja2 variables (`{{ }}`, `@variable`, `$variable`), and HTML content embedded in prompts.
Markdown code blocks tagged as `mthds` or `toml` also receive syntax highlighting when the extension is active.
### Semantic Tokens
Beyond TextMate grammar-based highlighting, the extension provides 7 semantic token types that distinguish MTHDS-specific elements:
| Token type | Applies to | Visual hint |
|------------|-----------|-------------|
| `mthdsConcept` | Concept names (e.g., `ContractClause`, `Text`) | Type color |
| `mthdsPipeType` | Pipe type values (e.g., `PipeLLM`, `PipeSequence`) | Type color, bold |
| `mthdsDataVariable` | Data variables in prompts | Variable color |
| `mthdsPipeName` | Pipe names in references | Function color |
| `mthdsPipeSection` | Pipe section headers (`[pipe.my_pipe]`) | Keyword color, bold |
| `mthdsConceptSection` | Concept section headers (`[concept.MyConcept]`) | Keyword color, bold |
| `mthdsModelRef` | Model field references (`$preset`, `@alias`) | Variable color, bold |
Semantic tokens are enabled by default. To toggle them:
- `pipelex.mthds.semanticTokens` — MTHDS-specific semantic tokens.
- `pipelex.syntax.semanticTokens` — TOML table/array key tokens.
### Formatting
The extension includes a built-in formatter for `.mthds` and `.toml` files. It uses the same engine as the `plxt` CLI (see [Formatting & Linting](formatting-linting.md)). Format on save works out of the box.
Formatting options are configurable in VS Code settings under `pipelex.formatter.*` (e.g., `alignEntries`, `columnWidth`, `trailingNewline`).
### Schema Validation
The extension supports JSON Schema-based validation and completion for TOML files. When the MTHDS JSON Schema is configured (see [MTHDS JSON Schema](json-schema.md)), the editor provides:
- Autocomplete suggestions for field names and values.
- Inline validation errors for invalid fields or types.
- Hover documentation for known fields.
Schema support is enabled by default (`pipelex.schema.enabled`).
### Additional Commands
The extension contributes several commands accessible via the Command Palette:
| Command | Description |
|---------|-------------|
| **TOML: Copy as JSON** | Copy selected TOML as JSON. |
| **TOML: Copy as TOML** | Copy selected text as TOML. |
| **TOML: Paste as JSON** | Paste clipboard content as JSON. |
| **TOML: Paste as TOML** | Paste clipboard content as TOML. |
| **TOML: Select Schema** | Choose a JSON Schema for the current TOML file. |
## Formatting & Linting
# Formatting & Linting
`plxt` is the CLI tool for formatting and linting `.mthds` and `.toml` files. It ensures consistent style across MTHDS projects.
## Installation
`plxt` is distributed as a standalone binary. Install it from the [`pipelex-tools` PyPI package](https://pypi.org/project/pipelex-tools/) (`uv tool install pipelex-tools`), or use the bundled version included with the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=pipelex.pipelex).
## Formatting
Format `.mthds` and `.toml` files in place:
```bash
# Format all .mthds and .toml files in the current directory (recursive)
plxt format .
# Format a single file
plxt format contract_analysis.mthds
# Format and see what changed (check mode — exits non-zero if changes needed)
plxt format --check .
```
The `plxt format` command (also available as `plxt fmt`) aligns entries, normalizes whitespace, and ensures consistent TOML style. Files are modified in place.
## Linting
Lint `.mthds` and `.toml` files for structural issues:
```bash
# Lint all files in the current directory
plxt lint .
# Lint a single file
plxt lint contract_analysis.mthds
```
The `plxt lint` command checks for TOML structural issues and reports errors.
## Configuration
`plxt` reads its configuration from a `.pipelex/plxt.toml` file in the project root or a parent directory. This file controls formatting rules (alignment, column width, trailing commas, etc.) and can define per-file-type overrides.
A basic configuration:
```toml
[formatting]
align_entries = true
column_width = 100
trailing_newline = true
array_trailing_comma = true
```
For the full list of configuration options, see the Pipelex documentation.
## Editor Integration
When the VS Code extension is installed, `plxt` formatting runs automatically on save. The extension uses the same formatting engine, so files formatted via CLI and editor produce identical results.
## MTHDS JSON Schema
# MTHDS JSON Schema
The MTHDS standard includes a machine-readable JSON Schema that describes the structure of `.mthds` files. Tools and editors can use this schema for validation, autocompletion, and documentation.
## What It Covers
The schema defines the complete structure of an `.mthds` bundle:
- **Header fields**: `domain`, `description`, `system_prompt`, `main_pipe`.
- **Concept definitions**: both simple (string) and structured forms, including `structure` fields, `refines`, and all field types (`text`, `integer`, `number`, `boolean`, `date`, `list`, `dict`, `concept`) and the `choices` enum mechanism.
- **Pipe definitions**: all pipe types with their specific fields — `PipeLLM`, `PipeStructure`, `PipeFunc`, `PipeImgGen`, `PipeExtract`, `PipeSearch`, `PipeCompose`, `PipeSequence`, `PipeParallel`, `PipeCondition`, `PipeBatch`.
- **Sub-pipe blueprints**: the `steps`, `branches`, `outcomes`, and `construct` structures used by controllers and PipeCompose.
- **Inline model settings**: the `LLMSetting`, `ImgGenSetting`, `ExtractSetting`, and `SearchSetting` objects that can be used in place of string model references.
## Schema Version
The schema is auto-generated from the MTHDS bundle data model. The current version is noted in the schema's `$comment` field. The hosted schema always corresponds to the latest released version of the MTHDS standard.
## Where to Find It
The schema is distributed with the tools that use it:
- **VS Code extension** — the Pipelex extension bundles the schema and uses it for autocompletion and inline validation.
- **`plxt` CLI** — the `plxt` binary includes the schema for local validation.
- **`pipelex-tools` PyPI package** — the schema is included in the Python distribution.
The schema is also hosted at a stable URL for direct use by editors and other tooling:
**Hosted URL:** [`https://mthds.ai/mthds_schema.json`](https://mthds.ai/mthds_schema.json)
## How to Use It
### With the VS Code Extension
The Pipelex VS Code extension includes the schema and uses it automatically for autocompletion and inline validation of `.mthds` files. No configuration is required.
### With Other Editors
Any editor that supports JSON Schema for TOML can use the MTHDS schema. Configure your editor's TOML language server to associate `.mthds` files with the schema URL `https://mthds.ai/mthds_schema.json`.
### For Tooling
The schema can be used programmatically for:
- Building custom validators for `.mthds` files.
- Generating documentation from the schema structure.
- Implementing autocompletion in non-VS Code editors.
For detailed guidance on building editor support, see [For Implementers: Building Editor Support](../implementers/editor-support.md).
# Guides
## Write Your First Method
# Write Your First Method
This guide walks you through creating a working `.mthds` file from scratch. By the end, you will have a method that generates a short summary from a text input.
## Prerequisites
- A text editor with MTHDS support. Install the [VS Code extension](../tooling/editor-support.md) for the best experience.
- The `plxt` CLI installed for formatting (see [Formatting & Linting](../tooling/formatting-linting.md)).
- The `mthds` CLI installed for validation.
## Step 1: Create a `.mthds` File
Create a new file called `summarizer.mthds` and add a domain header:
```toml
domain = "summarization"
description = "Text summarization methods"
```
Every bundle starts with a `domain` — a namespace for the concepts and pipes you will define. The domain name uses `snake_case` segments separated by dots.
## Step 2: Define a Concept
Add a concept to describe the kind of data your method produces:
```toml
domain = "summarization"
description = "Text summarization methods"
[concept]
Summary = "A concise summary of a longer text"
```
This declares a simple concept called `Summary`. It has no internal structure — it is a semantic label that gives meaning to the data your pipe produces.
Concept codes use `PascalCase` (e.g., `Summary`, `ContractClause`, `CandidateProfile`).
## Step 3: Define a Pipe
Add a pipe that takes text input and produces a summary:
```toml
domain = "summarization"
description = "Text summarization methods"
main_pipe = "summarize"
[concept]
Summary = "A concise summary of a longer text"
[pipe.summarize]
type = "PipeLLM"
description = "Summarize the input text in 2-3 sentences"
inputs = { text = "Text" }
output = "Summary"
prompt = """
Summarize the following text in 2-3 concise sentences. Focus on the key points.
@text
"""
```
Here is what each field does:
- `type = "PipeLLM"` — this pipe uses a large language model to generate output.
- `inputs = { text = "Text" }` — the pipe accepts one input called `text`, of the native `Text` type.
- `output = "Summary"` — the pipe produces a `Summary` concept.
- `prompt` — the LLM prompt template. `@text` is shorthand for `{{ text }}`, injecting the input variable.
The `main_pipe = "summarize"` header marks this pipe as the bundle's primary entry point.
## Step 4: Format Your File
Run the formatter to ensure consistent style:
```bash
plxt fmt summarizer.mthds
```
The formatter aligns entries, normalizes whitespace, and ensures your file follows MTHDS style conventions.
## Step 5: Validate
Validate your bundle:
```bash
mthds validate summarizer.mthds
```
If everything is correct, you will see a success message. If there are errors — a misspelled concept reference, an unused input, a missing required field — the validator reports them with specific messages.
## The Complete File
```toml
domain = "summarization"
description = "Text summarization methods"
main_pipe = "summarize"
[concept]
Summary = "A concise summary of a longer text"
[pipe.summarize]
type = "PipeLLM"
description = "Summarize the input text in 2-3 sentences"
inputs = { text = "Text" }
output = "Summary"
prompt = """
Summarize the following text in 2-3 concise sentences. Focus on the key points.
@text
"""
```
This file works as a standalone bundle — no manifest, no package, no dependencies. To run it:
```bash
mthds run summarizer.mthds
```
!!! note "Runtime required"
Execution requires an MTHDS-compatible runtime. The reference runtime is [Pipelex](https://github.com/Pipelex/pipelex).
## File Naming Conventions
When organizing `.mthds` files in a project:
- Use `snake_case` for file names: `invoice_processing.mthds`, `cv_analysis.mthds`.
- Match the file name to the bundle's domain when practical. A bundle with `domain = "invoice_processing"` lives naturally in `invoice_processing.mthds`.
- Use the `.mthds` extension — it is required by the toolchain for validation and formatting.
## Next Steps
- Add more concepts and pipes to your bundle. See [The Language](../language/bundles.md) for the full set of pipe types and concept features.
- When you are ready to distribute your methods, see [Create a Package](../guides/create-package.md).
## Create a Package
# Create a Package
This guide walks you through turning a standalone bundle into a distributable MTHDS package.
## What You Start With
You have one or more `.mthds` files that work on their own:
```
my-methods/
├── summarizer.mthds
└── classifier.mthds
```
## Step 1: Initialize the Manifest
Run `mthds pkg init` from the package directory:
```bash
cd my-methods
mthds pkg init
```
This scans all `.mthds` files, extracts domains and pipe names, and generates a `METHODS.toml` skeleton:
```toml
[package]
address = "example.com/yourorg/my_methods"
version = "0.1.0"
description = "Package generated from 2 .mthds file(s)"
[exports.summarization]
pipes = ["summarize"]
[exports.classification]
pipes = ["classify_document"]
```
## Step 2: Set the Package Address
Edit the `address` field to your actual repository location:
```toml
[package]
address = "github.com/yourorg/my-methods"
version = "0.1.0"
description = "Text summarization and document classification methods"
```
The address must start with a hostname (containing at least one dot), followed by a path. It doubles as the fetch location when other packages depend on yours.
## Step 3: Configure Exports
Review the `[exports]` section. The generated manifest exports all pipes found during scanning. Narrow it down to your public API:
```toml
[exports.summarization]
pipes = ["summarize"]
[exports.classification]
pipes = ["classify_document"]
```
Pipes not listed in `[exports]` are private — they are implementation details invisible to consumers. Pipes declared as `main_pipe` in a bundle header are auto-exported regardless of whether they appear here.
Concepts are always public — they do not need to be listed.
## Step 4: Add Metadata
Add optional but recommended fields:
```toml
[package]
address = "github.com/yourorg/my-methods"
version = "0.1.0"
description = "Text summarization and document classification methods"
authors = ["Your Name "]
license = "MIT"
mthds_version = ">=1.0.0"
```
## Step 5: Validate
Verify your package is well-formed:
```bash
mthds validate --all
```
This validates all pipes across all bundles in the package, checking concept references, pipe references, and visibility rules.
## The Result
Your package directory now looks like:
```
my-methods/
├── METHODS.toml
├── summarizer.mthds
└── classifier.mthds
```
You have a distributable package with a globally unique address, versioned identity, and controlled exports. Other packages can now depend on it.
## See Also
- [The Manifest](../packages/manifest.md) — full reference for `METHODS.toml` fields.
- [Exports & Visibility](../packages/exports-visibility.md) — how visibility rules work.
- [Use Dependencies](use-dependencies.md) — how to depend on other packages.
## Use Dependencies
# Use Dependencies
This guide shows how to add dependencies on other MTHDS packages and use their concepts and pipes in your bundles.
## Step 1: Add a Dependency
Use `mthds pkg add` to add a dependency to your `METHODS.toml`:
```bash
mthds pkg add github.com/mthds/document-processing --version "^1.0.0"
```
This adds an entry to the `[dependencies]` section:
```toml
[dependencies]
document_processing = { address = "github.com/mthds/document-processing", version = "^1.0.0" }
```
The alias (`document_processing`) is auto-derived from the last segment of the address. To choose a shorter alias:
```bash
mthds pkg add github.com/mthds/document-processing --alias docproc --version "^1.0.0"
```
```toml
[dependencies]
docproc = { address = "github.com/mthds/document-processing", version = "^1.0.0" }
```
## Step 2: Resolve and Lock
Generate the lock file to pin exact versions:
```bash
mthds pkg lock
```
Then install the dependencies into the local cache:
```bash
mthds pkg install
```
## Step 3: Use Cross-Package References
In your `.mthds` files, reference the dependency's concepts and pipes using the `->` syntax:
```toml
domain = "analysis"
[pipe.analyze_document]
type = "PipeSequence"
description = "Extract pages from a document and analyze them"
inputs = { document = "Document" }
output = "AnalysisResult"
steps = [
{ pipe = "docproc->extraction.extract_text", result = "pages" },
{ pipe = "process_pages", result = "analysis" },
]
```
The reference `docproc->extraction.extract_text` reads as: "from the package aliased as `docproc`, get the pipe `extract_text` in the `extraction` domain."
Cross-package concept references work the same way:
```toml
[concept.DetailedPage]
description = "An enriched page with additional metadata"
refines = "docproc->extraction.ExtractedPage"
```
## Step 4: Validate
```bash
mthds validate --all
```
Validation checks that:
- The alias `docproc` exists in `[dependencies]`.
- The pipe `extract_text` exists in the `extraction` domain of the resolved dependency.
- The pipe is exported by the dependency (listed in its `[exports]` or declared as `main_pipe`).
## Using Local Path Dependencies
During development, you can point a dependency to a local directory instead of fetching it remotely:
```bash
mthds pkg add github.com/mthds/document-processing --path ../document-processing --version "^1.0.0"
```
```toml
[dependencies]
docproc = { address = "github.com/mthds/document-processing", version = "^1.0.0", path = "../document-processing" }
```
Local path dependencies are resolved from the filesystem at load time. They are not resolved transitively and are excluded from the lock file.
## Updating Dependencies
To update all dependencies to their latest compatible versions:
```bash
mthds pkg update
```
This performs a fresh resolution, writes an updated `methods.lock`, and shows a diff of what changed.
## See Also
- [Dependencies](../packages/dependencies.md) — full reference for dependency fields and version constraints.
- [Cross-Package References](../packages/cross-package-references.md) — the `->` syntax explained.
- [Version Resolution](../packages/version-resolution.md) — how Minimum Version Selection works.
## Discover Methods
# Discover Methods
This guide shows how to search for and discover existing MTHDS methods — by text, by domain, or by typed signature.
## Searching by Text
The simplest search is a text query:
```bash
mthds pkg search "contract"
```
This searches concepts and pipes for the term "contract" (case-insensitive substring match) and displays matching results in tables showing package, name, domain, description, and export status.
To narrow results:
```bash
# Show only concepts
mthds pkg search "contract" --concept
# Show only pipes
mthds pkg search "contract" --pipe
# Filter by domain
mthds pkg search "extract" --domain legal.contracts
```
## Searching by Type ("I Have X, I Need Y")
MTHDS enables something that text-based discovery cannot: **type-compatible search**. Instead of searching by name, you search by what data types a pipe accepts or produces.
### "What can I do with X?"
Find all pipes that accept a given concept:
```bash
mthds pkg search --accepts Document
```
This returns every pipe whose input type is `Document` or a concept that `Document` refines. Because the search understands the concept refinement hierarchy, it finds pipes you might not discover through text search alone.
### "What produces Y?"
Find all pipes that produce a given concept:
```bash
mthds pkg search --produces NonCompeteClause
```
### Combining Accepts and Produces
Find pipes that bridge two types:
```bash
mthds pkg search --accepts Document --produces NonCompeteClause
```
## Exploring the Know-How Graph
For more advanced queries — multi-step chains, compatibility checks, auto-composition — use the `mthds pkg graph` command.
### Finding Chains
When no single pipe transforms X into Y, the graph can find multi-step chains:
```bash
mthds pkg graph \
--from "__native__::native.Document" \
--to "github.com/acme/legal-tools::legal.contracts.NonCompeteClause"
```
This might discover a chain like:
```
1. extract_pages -> analyze_content -> extract_clause
```
With `--compose`, it generates a ready-to-use MTHDS snippet:
```bash
mthds pkg graph \
--from "__native__::native.Document" \
--to "github.com/acme/legal-tools::legal.contracts.NonCompeteClause" \
--compose
```
### Checking Compatibility
Before wiring two pipes together, verify they are type-compatible:
```bash
mthds pkg graph --check "pkg_a::extract_pages,pkg_a::analyze_content"
```
This reports whether the output of the first pipe matches any input of the second.
## Searching Cached Packages
By default, search and graph commands operate on the current project. To search across all cached packages (everything you have installed):
```bash
mthds pkg search "scoring" --cache
mthds pkg graph --from "__native__::native.Text" --cache
```
## Inspecting a Package
To see the full contents of a specific package — its domains, concepts, and pipe signatures:
```bash
mthds pkg inspect github.com/acme/legal-tools
```
This displays detailed tables for every domain, concept (including structure fields and refinement), and pipe (including inputs, outputs, and export status).
## Building the Index
Before searching, you may want to build or refresh the package index:
```bash
# Index the current project
mthds pkg index
# Index all cached packages
mthds pkg index --cache
```
The index is built automatically when you run search or graph commands, but building it explicitly lets you verify what packages are available.
## See Also
- [The Know-How Graph](../know-how-graph/index.md) — how typed signatures enable semantic discovery.
- [Cross-Package References](../packages/cross-package-references.md) — how to use discovered pipes in your bundles.
- [Use Dependencies](use-dependencies.md) — how to add a discovered package as a dependency.
- [The Registry](../packages/registry.md) — query remote registries for packages beyond your local cache.
- [Registry Search](../packages/registry-search.md) — type-aware search semantics and concept compatibility rules.
# For Implementers
## Building a Runtime
# Building a Runtime
This page describes how to build a runtime that loads, validates, and executes MTHDS bundles and packages. The specification defines *what* must hold; this page describes *how* the reference implementation achieves it, as guidance for alternative implementations.
## High-Level Architecture
A compliant MTHDS runtime has four main subsystems:
1. **Parser** — reads `.mthds` TOML files into an in-memory bundle model.
2. **Loader** — discovers manifests, resolves dependencies, assembles a library of bundles.
3. **Validator** — checks all structural, naming, reference, and visibility rules.
4. **Executor** — runs pipes by dispatching to operator backends (LLM, function, image generation, extraction, composition) and orchestrating controllers.
The first three are specified by the standard; the fourth is implementation-specific (the standard defines *what* a pipe does, not *how*).
## Parsing .mthds Files
A `.mthds` file is valid TOML. Parse it with any compliant TOML parser, then validate the resulting structure against the MTHDS data model.
**Recommended approach:**
1. Parse the TOML into a generic dictionary.
2. Extract header fields (`domain`, `description`, `system_prompt`, `main_pipe`).
3. Extract the `concept` table — a mix of simple declarations (string values) and structured declarations (sub-tables with `description`, `structure`, `refines`).
4. Extract `pipe` sub-tables. Each pipe has a `type` field that determines the discriminated union variant (one of the supported pipe types).
5. Validate all fields against the rules in the [Specification](../spec/mthds-format.md).
The reference implementation uses Pydantic's discriminated union on the `type` field to dispatch pipe parsing:
```
PipeBlueprintUnion = PipeFuncBlueprint
| PipeImgGenBlueprint
| PipeComposeBlueprint
| PipeLLMBlueprint
| PipeStructureBlueprint
| PipeExtractBlueprint
| PipeSearchBlueprint
| PipeBatchBlueprint
| PipeConditionBlueprint
| PipeParallelBlueprint
| PipeSequenceBlueprint
```
This means an invalid `type` value is rejected at parse time, before any field-level validation occurs.
## Manifest Discovery
When loading a bundle, the runtime must locate the package manifest (`METHODS.toml`) by walking up the directory tree:
```
function find_manifest(bundle_path):
current = parent_directory(bundle_path)
while true:
if "METHODS.toml" exists in current:
return parse_manifest(current / "METHODS.toml")
if ".git" directory exists in current:
return null // stop at repository boundary
parent = parent_directory(current)
if parent == current:
return null // filesystem root
current = parent
```
If no manifest is found, the bundle is treated as a standalone bundle: all pipes are public, no dependencies are available beyond native concepts, and the bundle is not distributable.
## Loading a Package
Loading a package involves these steps in order:
1. **Parse the manifest** — read `METHODS.toml` and validate all fields (address, version, dependencies, exports). Reject immediately on any parse or validation error.
2. **Discover bundles** — recursively find all `.mthds` files under the package root.
3. **Parse all bundles** — parse each `.mthds` file into a bundle blueprint. Collect parse errors.
4. **Resolve dependencies** — for each dependency in the manifest:
- If it has a `path` field, resolve from the local filesystem (non-transitive).
- If it is remote, resolve via VCS (transitive, with cycle detection and diamond handling).
5. **Build the library** — assemble all parsed bundles (local and dependency) into a library structure indexed by domain and package.
6. **Validate references** — check that all concept and pipe references resolve correctly, following the [Namespace Resolution Rules](../spec/namespace-resolution.md).
7. **Validate visibility** — check that cross-domain and cross-package pipe references respect export rules.
## Working Memory
Controllers orchestrate pipes through **working memory** — a key-value store that accumulates results as a pipeline executes.
When a `PipeSequence` runs, each step's output is stored under its `result` name. Subsequent steps can consume any previously stored value. The final step's output (or the value matching the sequence's `output` concept) becomes the sequence's output.
Working memory is scoped to a pipeline execution. Each top-level `mthds run` invocation starts with a fresh working memory containing only the declared inputs.
## Concept Refinement at Runtime
Concept refinement establishes a type-compatibility relationship. When a pipe declares `inputs = { doc = "ContractClause" }`, any concept that refines `ContractClause` (directly or transitively) is an acceptable input.
A runtime must build and query a refinement graph:
```
function is_compatible(actual_concept, expected_concept):
if actual_concept == expected_concept:
return true
if actual_concept is a native concept and expected_concept == "Anything":
return true
parent = refinement_parent(actual_concept)
if parent is null:
return false
return is_compatible(parent, expected_concept)
```
The refinement graph is built during loading by following `refines` fields across all loaded concepts (including cross-package refinements).
## Output Validation
A compliant runtime validates the output of every pipe against the declared output concept's structure at every intermediate step — not just at the method level. This ensures that errors surface at the step that produces incorrect output, not downstream where the symptoms are harder to trace.
**Recommended approach:**
1. After a pipe produces output, resolve the output concept's definition (including its `structure` fields if any).
2. Validate the produced data against the concept's type and field constraints — required fields, field types (`text`, `integer`, `boolean`, `list`, `dict`, `number`, `date`, `concept`), and any `choices` enums.
3. If validation fails, report the error with the pipe code and step index, and halt execution of the current pipeline.
Validation libraries such as Pydantic (Python) or Zod (TypeScript) are natural fits for implementing these checks. Beyond mapping MTHDS concept structures to schema definitions, these libraries also support custom validation logic — expressed in Python or TypeScript — that goes beyond what the MTHDS standard defines.
## Model References
MTHDS defines several forms of model reference (`$` preset, `@` alias, `~` waterfall, and bare handle) that method authors use in the `model` field of `PipeLLM`, `PipeStructure`, `PipeImgGen`, `PipeExtract`, and `PipeSearch`. See [Model References](../language/model-references.md) for the full description and examples.
A runtime must resolve each form to a concrete model configuration. The recommended approach:
1. **Parse the prefix** — inspect the first character of the `model` string to determine the reference kind (`$`, `@`, `~`, or no prefix).
2. **Look up in a registry** — resolve the name (after stripping the prefix) against the appropriate registry:
- `$` → preset registry (returns a model handle plus parameters such as temperature, max tokens, quality).
- `@` → alias registry (returns a model handle).
- `~` → waterfall registry (returns an ordered list of model handles to try in sequence).
- No prefix → treat the string as a direct model handle.
3. **Return the model configuration** — pass the resolved handle (and any associated parameters) to the operator backend.
A compliant runtime may implement model references differently — or not at all, treating the `model` field as a direct model identifier. The standard requires only that the field be a string.
## Template Blueprint (Advanced PipeCompose)
When the `template` field of a `PipeCompose` pipe is a table (rather than a plain string), it is a **template blueprint** with additional rendering options:
| Field | Type | Description |
|-------|------|-------------|
| `template` | string | The Jinja2 template source. Required. |
| `category` | string | Determines which Jinja2 filters and rendering rules apply. Values: `basic`, `expression`, `html`, `markdown`, `mermaid`, `llm_prompt`, `img_gen_prompt`. |
| `templating_style` | object or null | Controls tag style and text formatting during rendering. |
| `extra_context` | object or null | Additional variables injected into the template rendering context beyond the pipe's declared inputs. |
The `category` field influences which Jinja2 filters are available. For example, `html` templates get HTML-specific filters, while `llm_prompt` templates get prompt-specific filters. The reference implementation registers different filter sets per category.
**Shorthand preprocessor:** A compliant runtime SHOULD expand the `$`, `@`, and `@?` shorthand patterns into their Jinja2 equivalents before template rendering. See the [specification](../spec/mthds-format.md#template-mode) for the normative expansion rules. The preprocessor runs on the `template` field of PipeCompose, the `prompt` and `system_prompt` fields of PipeLLM, and the `prompt` field of PipeImgGen and PipeSearch.
**Filter registration per category:** The reference implementation registers the following Jinja2 filters based on the template category:
| Category | Registered Filters |
|----------|-------------------|
| `basic` | `format`, `tag` |
| `expression` | *(none)* |
| `html` | `format`, `tag`, `escape_script_tag` |
| `markdown` | `format`, `tag`, `escape_script_tag` |
| `mermaid` | *(none)* |
| `llm_prompt` | `format`, `tag`, `with_images` |
| `img_gen_prompt` | `format`, `tag`, `with_images` |
See [Pipes — Operators: Template Mode](../language/pipes-operators.md#template-mode) for the user-facing reference on template categories, filters, and shorthand syntax.
A compliant runtime must support the plain string form of `template`. The table form with `category`, `templating_style`, and `extra_context` is an advanced feature that implementations may support progressively.
## Exposing a Runner over HTTP
A runtime becomes a network-accessible **runner** by implementing the [MTHDS Protocol](../spec/protocol.md) — the minimal HTTP contract of five routes: `POST /execute`, `POST /start`, `POST /validate`, `GET /models`, `GET /version`. The normative artifact is the protocol's [OpenAPI document](../spec/openapi/mthds-protocol.openapi.yaml).
The protocol deliberately excludes everything that is not "run a method": no run store, no completion channel for async runs (webhooks or polling are implementation extensions), no users, no billing, no catalog. Implementations may extend the surface with extra routes or optional request properties, but must not change the protocol routes' shapes — see the [extension policy](../spec/protocol.md#extension-policy).
The reference implementation is `pipelex-api` (MIT, self-hostable Docker image), which implements the protocol and extends it with stateless `/build/*` authoring helpers.
`/validate` is a diagnostic endpoint: a produced verdict — valid or invalid — always rides a 200, discriminated on `is_valid` (the protocol also declares the runnability facts `is_runnable` / `pending_signatures`; a non-2xx means *no verdict could be produced*, not an invalid bundle). Beyond those declared fields a runner returns its own verification artifacts as implementation-defined additional properties on the valid arm. The reference implementation (Pipelex) returns `pipe_io_contracts` — a map keyed by namespaced `pipe_ref` (`.`) giving each pipe's resolved input and output contracts (the concept and multiplicity of every input and of the output). The map contains entries only for pipes defined in the validated package, not its transitive dependencies (whose pipes resolve under the package-qualified `alias->domain.pipe_code` form), so the `.` keys cannot collide across packages. The name is documented here only to encourage convergence among implementations; it is not part of the protocol, and other runners may expose their own artifacts under their own keys. On the invalid arm (`is_valid: false`) the protocol requires a non-empty `validation_errors[]` (one diagnostic per failure); each item's `category` is implementation-defined — the reference implementation (Pipelex) narrows it to its own closed vocabulary.
## Validation Rules
# Validation Rules
This page consolidates all validation rules from the [Specification](../spec/mthds-format.md) into an ordered checklist for implementers. Rules are grouped by the stage at which they should be enforced.
## Stage 1: TOML Parsing
Before any MTHDS-specific validation, the file must be valid TOML.
- The file MUST be valid UTF-8-encoded TOML.
- A `.mthds` file MUST have the `.mthds` extension.
- `METHODS.toml` MUST be named exactly `METHODS.toml`.
- `methods.lock` MUST be named exactly `methods.lock`.
## Stage 2: Bundle Structural Validation
After parsing TOML into a dictionary, validate the bundle structure:
1. `domain` MUST be present.
2. `domain` MUST be a valid domain code: one or more `snake_case` segments (`[a-z][a-z0-9_]*`) separated by `.`.
3. `main_pipe`, if present, MUST be `snake_case` and MUST reference a pipe defined in the same bundle.
4. Concept codes MUST be `PascalCase` (`[A-Z][a-zA-Z0-9]*`).
5. Concept codes MUST NOT match any native concept code (`Dynamic`, `Text`, `Image`, `Document`, `Html`, `TextAndImages`, `Number`, `Page`, `JSON`, `SearchResult`, `Anything`).
6. Pipe codes MUST be `snake_case` (`[a-z][a-z0-9_]*`).
7. `refines` and `structure` MUST NOT both be set on the same concept.
## Stage 3: Concept Field Validation
For each field in a concept's `structure`:
1. `description` MUST be present.
2. If `type` is omitted, `choices` MUST be non-empty.
3. `type = "dict"` requires both `key_type` and `value_type`.
4. `type = "concept"` requires `concept_ref` and forbids `default_value`.
5. `type = "list"` with `item_type = "concept"` requires `item_concept_ref`.
6. `concept_ref` MUST NOT be set unless `type = "concept"`.
7. `item_concept_ref` MUST NOT be set unless `item_type = "concept"`.
8. `default_value` type MUST match the declared `type`.
9. If `choices` is set and `default_value` is present, `default_value` MUST be in `choices`.
10. Field names MUST NOT start with `_`.
## Stage 4: Pipe Type-Specific Validation
Each pipe type has specific rules:
**PipeLLM:**
- All prompt and system_prompt variables MUST have matching inputs.
- All inputs MUST be referenced in prompt or system_prompt.
**PipeStructure:**
- `inputs` MUST contain exactly one entry.
- The single input concept MUST be `Text` or a concept that refines `Text`.
- `output` MUST NOT be `Text` and MUST NOT be a concept that refines `Text`.
- `output` MAY use multiplicity (`Foo`, `Foo[]`, `Foo[N]`).
**PipeFunc:**
- `function_name` MUST be present and non-empty.
**PipeImgGen:**
- `prompt` MUST be present.
- All prompt variables MUST have matching inputs.
**PipeExtract:**
- `inputs` MUST contain exactly one entry.
- `output` MUST be `"Page[]"`.
**PipeSearch:**
- `prompt` MUST be present.
- All prompt variables MUST have matching inputs.
- `output` MUST be `SearchResult` or a concept that refines `SearchResult`.
**PipeCompose:**
- Exactly one of `template` or `construct` MUST be present.
- `output` MUST NOT use multiplicity brackets (`[]` or `[N]`).
- All template/construct variables MUST have matching inputs.
**PipeSequence:**
- `steps` MUST have at least one entry.
- `nb_output` and `multiple_output` MUST NOT both be set on the same step.
- `batch_over` and `batch_as` MUST either both be present or both be absent.
- `batch_over` and `batch_as` MUST NOT be the same value.
**PipeParallel:**
- At least one of `add_each_output` or `combined_output` MUST be set.
**PipeCondition:**
- Exactly one of `expression_template` or `expression` MUST be present.
- `outcomes` MUST have at least one entry.
**PipeBatch:**
- `input_list_name` MUST be in `inputs`.
- `input_item_name` MUST NOT be empty.
- `input_item_name` MUST NOT equal `input_list_name`.
- `input_item_name` MUST NOT equal any key in `inputs`.
## Stage 5: Reference Validation (Bundle-Level)
Within a single bundle:
- Bare concept references MUST resolve to: a native concept, a concept in the current bundle, or a concept in the same domain (same package).
- Bare pipe references MUST resolve to: a pipe in the current bundle, or a pipe in the same domain (same package).
- Domain-qualified references MUST resolve within the current package.
- Cross-package references (`->` syntax) are deferred to package-level validation.
## Stage 6: Manifest Validation
For `METHODS.toml`:
1. `[package]` section MUST be present.
2. `address` MUST match the pattern `^[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+/[a-zA-Z0-9._/-]+$`.
3. `version` MUST be valid semver.
4. `description` MUST NOT be empty.
5. All dependency aliases MUST be unique and `snake_case`.
6. All dependency addresses MUST match the hostname/path pattern.
7. All dependency version constraints MUST be valid.
8. Domain paths in `[exports]` MUST be valid domain codes.
9. Domain paths in `[exports]` MUST NOT use reserved domains (`native`, `mthds`, `pipelex`).
10. All pipe codes in `[exports]` MUST be valid `snake_case`.
## Stage 7: Package-Level Validation
After loading all bundles and resolving dependencies:
1. Bundles MUST NOT declare a domain starting with a reserved segment.
2. Cross-package references MUST reference known dependency aliases.
3. Cross-package pipe references MUST target exported pipes.
4. Exported pipes MUST exist in the scanned bundles.
5. Same-domain concept and pipe code collisions across bundles are errors.
## Stage 8: Lock File Validation
For `methods.lock`:
1. Each entry's `version` MUST be valid semver.
2. Each entry's `hash` MUST match `sha256:[0-9a-f]{64}`.
3. Each entry's `source` MUST start with `https://`.
## Package Loading
# Package Loading
This page details the dependency resolution algorithm, library assembly, and namespace isolation mechanics.
## Dependency Resolution Algorithm
Dependency resolution is a recursive process that handles local paths, remote fetching, cycle detection, and diamond dependencies.
```
function resolve_all_dependencies(manifest, package_root):
local_resolved = []
remote_deps = []
for dep in manifest.dependencies:
if dep.path is not null:
local_resolved.append(resolve_from_filesystem(dep, package_root))
else:
remote_deps.append(dep)
resolved_map = {} // address -> resolved dependency
constraints = {} // address -> list of version constraints
resolution_stack = set() // for cycle detection
resolve_transitive_tree(remote_deps, resolution_stack, resolved_map, constraints)
return local_resolved + values(resolved_map)
```
**Key rules:**
- **Local path dependencies** are resolved directly from the filesystem. They are NOT resolved transitively — only the root package's local paths are honored.
- **Remote dependencies** are resolved transitively. If Package A depends on Package B, and B depends on Package C, then C is also resolved.
- **Cycle detection** uses a DFS stack set. If an address is encountered while already on the stack, the resolver reports a cycle error.
## Diamond Dependency Handling
Diamond dependencies occur when the same package is required by multiple dependents with different version constraints.
```
function resolve_diamond(address, all_constraints, available_tags):
parsed_constraints = [parse_constraint(c) for c in all_constraints]
for version in sorted(available_tags, ascending):
if all(constraint.matches(version) for constraint in parsed_constraints):
return version
error("No version satisfies all constraints")
```
This is Minimum Version Selection applied to multiple constraints simultaneously. The resolver:
1. Collects all version constraints from every dependent that requires the package.
2. Lists available version tags from the remote repository (cached to avoid repeated network calls).
3. Sorts versions in ascending order.
4. Selects the first version that satisfies ALL constraints.
When a diamond re-resolution picks a different version than previously resolved, the stale sub-dependency constraints contributed by the old version are recursively removed before re-resolving.
## VCS Fetching
Remote packages are fetched via Git with a three-tier resolution chain:
1. **Local cache check** — look in `~/.mthds/packages/{address}/{version}/`.
2. **VCS fetch** — if not cached, clone the repository:
- Map address to clone URL: prepend `https://`, append `.git`.
- List remote tags: `git ls-remote --tags {url}`.
- Filter tags that parse as valid semver (strip optional `v` prefix).
- Select version via MVS.
- Clone at the selected tag: `git clone --depth 1 --branch {tag}`.
3. **Cache storage** — store the cloned directory under `~/.mthds/packages/{address}/{version}/`, removing the `.git` directory.
Cache writes use a staging directory with atomic rename for safety against partial writes.
## Library Assembly
After resolving all dependencies, the runtime assembles a **library** — the complete set of loaded bundles indexed by domain and package:
```
Library:
local_bundles: domain -> list of bundle blueprints
dependency_bundles: (alias, domain) -> list of bundle blueprints
exported_pipes: (alias, domain) -> set of pipe codes
main_pipes: (alias, domain) -> pipe code
```
The library provides the lookup context for namespace resolution. When a pipe reference like `scoring_lib->scoring.compute_weighted_score` is encountered:
1. Find the dependency by alias `scoring_lib`.
2. Look up domain `scoring` in the dependency's bundles.
3. Find the pipe `compute_weighted_score`.
4. Verify it is exported (in the `[exports]` list or declared as `main_pipe`).
## Namespace Isolation
Packages isolate namespaces completely. Two packages declaring `domain = "recruitment"` have independent concept and pipe namespaces. The isolation boundary is the package, not the domain.
Within a single package, bundles sharing the same domain merge into a single namespace. Collisions (duplicate concept or pipe codes within the same domain of the same package) are errors.
The reference implementation enforces isolation through the library structure: lookups are always scoped to a specific package (identified by alias for dependencies, or "current package" for local references).
## Visibility Checking Algorithm
The visibility checker runs after library assembly:
```
function check_visibility(manifest, bundles):
exported_pipes = build_export_index(manifest)
main_pipes = build_main_pipe_index(bundles)
errors = []
// Check reserved domains
for bundle in bundles:
if bundle.domain starts with reserved segment:
errors.append(reserved domain error)
// Check intra-package cross-domain references
for bundle in bundles:
for (pipe_ref, context) in bundle.collect_pipe_references():
if pipe_ref is special outcome ("fail", "continue"):
skip
if pipe_ref is cross-package (contains "->"):
validate alias exists in dependencies
else:
ref = parse_pipe_ref(pipe_ref)
if ref is qualified and not same domain as bundle:
if ref.pipe_code not in exported_pipes[ref.domain]:
if ref.pipe_code != main_pipes[ref.domain]:
errors.append(visibility error)
return errors
```
The checker runs three passes:
1. **Reserved domain check** — ensures no bundle uses `native`, `mthds`, or `pipelex` as the first domain segment.
2. **Intra-package visibility** — ensures cross-domain pipe references target exported or main_pipe pipes.
3. **Cross-package alias validation** — ensures `->` references use aliases declared in `[dependencies]`.
## See Also
- [Specification: Namespace Resolution Rules](../spec/namespace-resolution.md) — the formal resolution algorithm.
- [The Package System: Version Resolution](../packages/version-resolution.md) — how MVS works.
## Building Editor Support
# Building Editor Support
This page describes how to build editor support for `.mthds` files — syntax highlighting, semantic tokens, schema validation, and formatting.
## TextMate Grammar
The primary mechanism for syntax highlighting is a TextMate grammar layered on top of TOML. The grammar recognizes MTHDS-specific constructs within the TOML structure.
**Scope hierarchy:**
The base scope is `source.mthds` (extending `source.toml`). Key MTHDS-specific scopes include:
- `meta.pipe-section.mthds` — `[pipe.]` table headers
- `meta.concept-section.mthds` — `[concept.]` table headers
- `entity.name.type.mthds` — concept codes in `PascalCase`
- `entity.name.function.mthds` — pipe codes in references
- `string.template.mthds` — prompt template strings
- `variable.other.jinja.mthds` — Jinja2 variables (`{{ }}`, `@var`, `$var`)
**Key patterns to recognize:**
1. **Pipe sections** — table headers matching `[pipe.]` or `[pipe..]`.
2. **Concept sections** — table headers matching `[concept.]` or `[concept..structure]`.
3. **Pipe type values** — string values that match the pipe type names (`PipeLLM`, `PipeSequence`, etc.) in the `type` field of pipe sections.
4. **Prompt templates** — multi-line strings containing Jinja2 syntax and `@variable` / `$variable` shorthand.
5. **Cross-package references** — strings containing `->` (the arrow separator for package-qualified references).
6. **Model references** — string values with `$` or `@` prefixes in the `model` field.
**Implementation approach:**
The reference implementation's TextMate grammar is structured as a set of injection grammars that layer on top of the TOML base grammar. This allows TOML syntax to remain correct while MTHDS-specific constructs receive additional semantic coloring.
## Semantic Token Types
Beyond TextMate grammar-based highlighting, an LSP-aware extension can provide semantic tokens for more precise highlighting. The reference implementation defines 7 MTHDS-specific semantic token types:
| Token Type | Description | Applied To |
|------------|-------------|------------|
| `mthdsConcept` | Concept names | `ContractClause`, `Text`, `Image`, concept references in `inputs`, `output`, `refines` |
| `mthdsPipeType` | Pipe type values | `PipeLLM`, `PipeSequence`, etc. in the `type` field |
| `mthdsDataVariable` | Data variables in prompts | `@variable_name`, `$variable_name`, `{{ variable }}` |
| `mthdsPipeName` | Pipe names in references | Pipe codes in `steps[].pipe`, `branch_pipe_code`, `outcomes`, etc. |
| `mthdsPipeSection` | Pipe section headers | The entire `[pipe.my_pipe]` header |
| `mthdsConceptSection` | Concept section headers | The entire `[concept.MyConcept]` header |
| `mthdsModelRef` | Model field references | Values in the `model` field (e.g., `$writing-factual`, `@default-text-from-pdf`) |
**Detection algorithm for semantic tokens:**
The semantic token provider parses the TOML document and walks the AST to identify MTHDS-specific elements. For each token, it determines the type based on:
1. **Context** — is this value inside a `[pipe.*]` section or a `[concept.*]` section?
2. **Field name** — is this the `type` field, the `model` field, a prompt field, an `inputs`/`output` field?
3. **Value pattern** — does the value match `PascalCase` (concept), `snake_case` (pipe), or have a `$`/`@` prefix (model ref)?
## Using the MTHDS JSON Schema
The MTHDS JSON Schema (`mthds_schema.json`) provides machine-readable validation for `.mthds` files. It is a standard JSON Schema document that describes the complete bundle structure.
**What the schema covers:**
- Header fields (`domain`, `description`, `system_prompt`, `main_pipe`)
- Concept definitions (simple and structured forms)
- All pipe types with their specific fields
- Sub-pipe blueprints (`steps`, `branches`, `outcomes`, `construct`)
- Field types and their constraints
**How to use it:**
1. **For validation** — feed the parsed TOML (as JSON) through a JSON Schema validator. This catches structural errors (wrong field types, missing required fields) without implementing MTHDS-specific validation logic.
2. **For autocompletion** — use the schema's `properties` and `enum` values to suggest field names and valid values.
3. **For hover documentation** — use the schema's `description` fields to show documentation on hover.
**Generating the schema:**
The reference implementation auto-generates the schema from the Pydantic data model (`PipelexBundleBlueprint`) using the `pipelex-dev generate-mthds-schema` command. This ensures the schema stays in sync with the implementation. Alternative implementations can use the published schema directly.
**Configuring schema association:**
In the `plxt.toml` configuration, associate `.mthds` files with the schema:
```toml
[[rule]]
include = ["**/*.mthds"]
[rule.schema]
url = "https://mthds.ai/mthds_schema.json"
```
## LSP Integration Points
The reference implementation includes an LSP server, available standalone via `plxt lsp stdio`. It is built on a fork of [taplo](https://github.com/tamasfe/taplo), extended with MTHDS-specific semantic tokens, validation, and navigation. It currently provides formatting, document symbols, folding, semantic tokens, schema-based validation and completion, and basic within-bundle go-to-definition. The LSP is bundled with the [`pipelex-tools`](https://pypi.org/project/pipelex-tools/) CLI and with the Pipelex VS Code extension ([source](https://github.com/Pipelex/vscode-pipelex), [Marketplace](https://marketplace.visualstudio.com/items?itemName=pipelex.pipelex), [Open VSX](https://open-vsx.org/extension/Pipelex/pipelex)).
The following integration points describe the full scope of MTHDS-aware language server capabilities. Each bullet notes the current coverage in the reference implementation:
- **Diagnostics** — run validation (Stages 2–7 from the [Validation Rules](validation-rules.md) page) and report errors as LSP diagnostics. *(Reference implementation: schema-level validation only.)*
- **Completion** — suggest pipe type names, native concept codes, field type names, concept codes from the current bundle, and pipe codes for references. *(Reference implementation: schema-based suggestions for field names and values.)*
- **Hover** — show concept descriptions, pipe signatures, and field documentation. *(Reference implementation: schema-based field documentation.)*
- **Go to Definition** — navigate from a concept/pipe reference to its definition (may span files for domain-qualified or cross-package references). *(Reference implementation: within-bundle navigation only.)*
- **Find References** — find all usages of a concept or pipe across bundles. *(Not yet implemented in the reference implementation.)*
- **Rename** — rename a concept or pipe code across all references in the package. *(Not yet implemented in the reference implementation.)*
## See Also
- [Tooling: Editor Support](../tooling/editor-support.md) — user-facing editor documentation.
- [Tooling: MTHDS JSON Schema](../tooling/json-schema.md) — user-facing schema documentation.
# About
## Roadmap
# Roadmap
The MTHDS standard is at version `1.0.0`. This page outlines planned and potential directions for future development.
## Near-Term
- **Registry reference implementation.** A reference implementation for the registry index, enabling `mthds pkg search` to query remote registries in addition to local packages.
- **Package signing.** Optional signed manifests for enterprise use, enabling verifiable authorship and integrity beyond SHA-256 content hashes.
- **Cross-package concept refinement validation at install time.** The specification allows validation of concept refinement across packages at both install time and load time. The current reference implementation validates at load time only. Install-time validation would detect breaking changes earlier.
## Medium-Term
- **Know-How Graph web interface.** A web-based explorer for the Know-How Graph, enabling visual navigation of concept hierarchies and pipe chains across the public ecosystem.
- **Proxy/mirror support.** Configurable proxy for package fetching, supporting speed, reliability, and air-gapped environments (similar to Go's `GOPROXY`).
## Long-Term
- **Conditional concept fields.** Allow concept structure fields to be conditionally present based on the values of other fields.
- **Runtime interoperability standard.** A specification for how different MTHDS runtimes can exchange concept instances, enabling cross-runtime pipe invocation.
## Contributing to the Roadmap
The roadmap is shaped by community needs. If you have a use case that the standard does not yet support, open an issue in the MTHDS standard repository. Proposals that include concrete `.mthds` examples demonstrating the need are especially helpful.
## Contributing to MTHDS
# Contributing to MTHDS
MTHDS is an open standard. Contributions are welcome — whether they are bug reports, specification clarifications, tooling improvements, or new packages.
## Ways to Contribute
### Report Issues
If you find an inconsistency in the specification, a bug in a tool, or an edge case that is not documented, open an issue in the MTHDS standard repository. Include:
- What you expected to happen.
- What actually happened.
- A minimal `.mthds` or `METHODS.toml` example that demonstrates the issue.
### Propose Specification Changes
Specification changes follow a structured process:
1. **Open a discussion** describing the problem and your proposed solution. Include concrete `.mthds` examples showing before/after.
2. **Draft the change** as a pull request against the specification. Normative changes use RFC 2119 language (`MUST`, `SHOULD`, `MAY`).
3. **Review** by the maintainers and community. Changes to the specification require careful consideration of backward compatibility.
4. **Merge and release** as a new minor or major version of the standard.
### Build Packages
The ecosystem grows through packages. Publish packages that solve real problems in your domain. Well-documented packages with clear concept hierarchies and typed pipe signatures make the Know-How Graph more useful for everyone.
### Build Tools
The standard is tool-agnostic. If you build an MTHDS-related tool — an alternative runtime, an editor extension, a registry implementation, a visualization tool — share it with the community.
## Coding Standards for the Reference Implementation
The reference implementation (Pipelex) has its own coding standards and contribution guidelines. See the Pipelex repository for details.
## License
The MTHDS standard specification is open. Implementations may use any license. The reference implementation's license is specified in its repository.
## Contributing
--8<-- "CONTRIBUTING.md"
## Code of Conduct
--8<-- "CODE_OF_CONDUCT.md"
## License
--8<-- "LICENSE"
## Changelog
--8<-- "CHANGELOG.md"