Mash
Mash is a self-hosted durable runtime for code-authored automations using workflows with agents. A workflow is an ordered pipeline of typed steps, a step is deterministic Python or one run of a harnessed agent, and control flow stays in code.
Mash gives you a Python AgentSpec contract for defining agents, a
WorkflowSpec for authoring step pipelines, a HostBuilder that composes both
into a deployable pool, a FastAPI server for deployment, and a CLI/API for
interacting with a running host.
It's designed around Host-to-Agent Protocol (H2A) that standardizes interactions between user applications and agents.
What Mash Provides
- Workflows: ordered pipelines of typed steps, durable and observable. A step is deterministic Python or one agent run; each step's output threads into the next step's input with schema checks at every edge. The pipeline is code you can read, diff, test, and replay.
- Agent harness: the agent loop runs inside a durable request engine with tools, skills, memory, and structured output. Requests are recorded as replayable runtime events; retries, restarts, and long-running work just work.
- Frontier and open-source models: built-in adapters for Anthropic, OpenAI,
and Gemini, and any open-source model served over a Chat Completions endpoint,
self-hosted with vLLM or Ollama or hosted on OpenRouter. Each agent picks its
model in one line of
build_llm(). - Self-hosted interfaces: HTTP API with streaming, CLI, and interactive REPL, all on one Postgres. Deploy locally, in Docker, or on any cloud.
- Multi-agent composition: define a primary agent, add specialized subagents, and compose workflows behind a single host. Agents delegate to each other without a separate coordination layer.
- Human-in-the-loop: agents can pause for approval or ask users questions mid-execution. Interactions survive host restarts.
- Observability: span trees, trace analysis, telemetry API, built-in dashboard, and CLI trace inspection. No external APM needed.
- Synthetic evals: generate a test dataset and scoring rubric from a host's declared capabilities, run experiments that snapshot the live host, and compare quality and cost across runs before the first user message. Datasets, rubrics, and experiment results live in the built-in dashboard.
┌─────────────────────────────────────────┐
│ Durable Request │
│ │
│ ┌─ context ─── memory ──┐ │
│ │ │ │
request ────────► │ │ Agent Loop │ ──► signals │
(cli/api) │ │ think → act → observe │ │ │
│ │ │ ▼ │
│ └─ tools ───── skills ──┘ structured │
workflow step ──► │ ▲ output │
(api/cli) │ │ user interaction │
│ ▼ (approval / ask-user) │
│ │
│ resumable · replayable │
└─────────────────────────────────────────┘
A request from a user and a step in a workflow ride the same durable loop.
See Mash under the hood for a deeper look at each capability, and the product brief for the pitch.
Quick Start
Install:
# install the library
uv add mashpy
# install the `mash` CLI on your PATH
uv tool install mashpy
Define your agents:
Each agent is an AgentSpec subclass. It names itself, picks an LLM, and
declares a system prompt, tools, skills and agent config.
## my_app/agents.py
from mash import AgentSpec
from mash.core.config import AgentConfig
from mash.core.llm import AnthropicProvider
from mash.skills import SkillRegistry
from mash.tools import ToolRegistry
class ConciergeAgent(AgentSpec):
def get_agent_id(self):
return "concierge"
def build_tools(self):
return ToolRegistry()
def build_skills(self):
return SkillRegistry()
def build_llm(self):
return AnthropicProvider(app_id="concierge")
def build_agent_config(self):
return AgentConfig(
app_id="concierge",
system_prompt=(
"You are the concierge. Answer the user directly, and "
"delegate research-heavy questions to the research subagent."
),
)
class ResearchAgent(AgentSpec):
def get_agent_id(self):
return "research"
def build_tools(self):
return ToolRegistry()
def build_skills(self):
return SkillRegistry()
def build_llm(self):
return AnthropicProvider(app_id="research")
def build_agent_config(self):
return AgentConfig(
app_id="research",
system_prompt="You handle research-heavy questions in depth.",
)
Author the workflow:
The workflow is the automation: an ordered pipeline of typed steps that code
owns end to end. Use a CodeStep for deterministic Python and an AgentStep
when the work needs an agent.
## my_app/workflows.py
from pydantic import BaseModel
from mash import AgentStep, CodeStep, StepContext, WorkflowSpec
class ResearchRequest(BaseModel):
topic: str
class ResearchPlan(BaseModel):
topic: str
questions: list[str]
class ResearchBrief(BaseModel):
summary: str
sources: list[str]
def plan_research(
request: ResearchRequest,
_context: StepContext,
) -> ResearchPlan:
return ResearchPlan(
topic=request.topic,
questions=[
f"What are the key facts about {request.topic}?",
f"What should a reader understand about {request.topic}?",
],
)
RESEARCH_BRIEF = WorkflowSpec(
workflow_id="research-brief",
input_model=ResearchRequest,
steps=[
CodeStep(
step_id="plan",
run=plan_research,
input=ResearchRequest,
output=ResearchPlan,
),
AgentStep(
step_id="research",
agent_id="research",
input=ResearchPlan,
output=ResearchBrief,
),
],
)
The CodeStep output becomes the AgentStep input. Mash validates both edges,
runs each step durably, and uses the last step's output as the workflow result.
Build the pool:
The pool is the unit of deploy: agents and workflows registered together.
## my_app/host.py
from mash import AgentMetadata, HostBuilder
from .agents import ConciergeAgent, ResearchAgent
from .workflows import RESEARCH_BRIEF
def build_pool():
pool = (
HostBuilder()
.agent(
ConciergeAgent(),
metadata=AgentMetadata(
display_name="Concierge",
description="Front-door agent that answers users and delegates.",
capabilities=["conversation", "delegation"],
usage_guidance="Default entry point for user requests.",
),
)
.agent(
ResearchAgent(),
metadata=AgentMetadata(
display_name="Research",
description="Handles research-heavy questions in depth.",
capabilities=["research", "analysis"],
usage_guidance="Use for questions that need digging.",
),
)
.workflow(RESEARCH_BRIEF)
.build()
)
return pool
A pool can also be workflows alone. A workflow of pure CodeSteps references
no agents, and the resulting pool serves only workflow runs:
def build_pool():
# EXPORT_METRICS is a WorkflowSpec of pure CodeSteps; no agents needed.
return HostBuilder().workflow(EXPORT_METRICS).build()
Configure the environment:
The host needs an LLM key and a Postgres URL for its durable runtime. Put
them in a .env file the host loads on start:
# .env
ANTHROPIC_API_KEY=sk-ant-...
MASH_DATABASE_URL=postgresql://user:pass@localhost:5432/mash
Start the host:
mash host serve --host-app my_app.host:build_pool --host 127.0.0.1 --port 8000
Browse available agents:
mash browse
Compose an assistant host with primary and subagents:
mash compose --host assistant --primary concierge --subagents research \
--workflows research-brief
Talk to the host or execute / commands using Mash repl:
mash repl --host assistant
Key Concepts
| Concept | What it is |
|---|---|
| AgentSpec | Abstract contract defining one agent (id, tools, skills, LLM, config) |
| HostBuilder | Fluent builder that composes agents, workflows, and hosts into a Pool |
| Pool | The deployed pool of role-less agents the API server runs |
| Host | A composition over the pool (primary + subagents + workflows), defined in code or dynamically over the API |
| ToolRegistry | Register callable tools; built-ins include Bash, AskUser, InvokeSubagent |
| SkillRegistry | Markdown instruction bundles loaded on demand via a meta-tool |
| LLMProvider | Adapters for Anthropic, OpenAI, and Gemini |
| OSSCompatibleProvider | Runs open-source models (Gemma, Qwen, DeepSeek) over any Chat Completions endpoint, self-hosted (vLLM, Ollama) or hosted (OpenRouter); chosen in build_llm() like any provider |
| WorkflowSpec | Ordered pipeline of typed steps (CodeStep / AgentStep); runs are durable and observable |
| Eval / Experiment | A generated dataset and rubric bound to a host; an experiment runs the dataset against the host, snapshots its composition, and scores results with an LLM judge |
Mash Pilot
Pilot is a command-line guide to the Mash codebase, built on the Mash SDK and
shipped in this repo at src/pilot/. Its agents specialize in
Mash's own modules — so instead of reading docs or grepping the source, you ask
Pilot and it answers from the actual source tree.
> Summarize how HostBuilder registers pooled agents and host compositions.
> Trace how an accepted request moves through AgentRuntime and RequestEngine.
> When is request.waiting emitted, and what does it mean for a busy session?
Quick start:
# 1. Start the host — one container, embedded Postgres, Mash source included
docker run -d --name pilot -p 8000:8000 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-v pilot-data:/var/lib/pilot \
ghcr.io/imsid/mashpy-pilot:latest
# 2. Install the CLI and ask
curl -fsSL https://raw.githubusercontent.com/imsid/mashpy/main/install.sh | sh
pilot repl --host guide
Add -e GITHUB_MCP_PAT=ghp_... to enable the guide's commit-inspection tools.
The pilot-data volume keeps the database durable across restarts.
The guide team — one agent per module:
| Agent | Owns |
|---|---|
pilot |
Shared/cross-cutting: core, tools, skills, logging, memory |
cli-copilot |
src/mash/cli — commands, REPL, terminal rendering |
api-copilot |
src/mash/api — HTTP routes, FastAPI |
mcp-copilot |
src/mash/mcp — MCP client/server, transport, tool adaptation |
runtime-copilot |
src/mash/runtime — request lifecycle, event sourcing, durability |
workflow-copilot |
src/mash/workflows — step pipelines, DBOS orchestration, resume, run status |
Scaffolding your own app: the guide carries build-mash-agent,
build-mash-workflow, and build-mash-host skills so it goes beyond
explaining Mash to scaffolding your application:
> Build me a support agent with a knowledge base search tool and human approval for refunds.
> Scaffold a multi-agent code reviewer with separate agents for security, style, and correctness.
> I need an agent that connects to my MCP server at localhost:3000 and uses Gemini.
Use pilot serve from a source install to run your own host, or point the CLI
at any Mash deployment with --api-base-url. See src/pilot/ as
a reference when structuring your own multi-agent app.
Build with a Coding Agent
This repo includes CLAUDE.md so coding agents like Claude Code,
Codex, and Cursor can scaffold a Mash-powered agent from a natural language
prompt. Copy it into your project or point your agent at this repo to get
started. The Pilot guide (above) also carries the build-mash-agent,
build-mash-workflow, and build-mash-host skills for interactive
scaffolding from the REPL.
Documentation
- Product brief — the pitch: why the application-to-agent seam needs a standard
- Mash under the hood — what Mash offers and where it fits
- Synthetic evals — datasets, rubrics, experiments, and scoring for the cold-start problem
- Deployment guide — Docker, cloud, horizontal scaling
- Building agent CLIs — custom CLI development
- CLAUDE.md — full SDK reference for coding agents
- Package overview — subsystem boundaries and module guides
- Contributing — development setup, tests, repo structure
Contributing
Contributions are welcome. See CONTRIBUTING.md for setup, tests, and the pull request flow, and SECURITY.md for reporting vulnerabilities. Release notes live in CHANGELOG.md.
License
Mash is licensed under the Apache License 2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mashpy-0.19.0.tar.gz.
File metadata
- Download URL: mashpy-0.19.0.tar.gz
- Upload date:
- Size: 404.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5331514fd38d4b2715f5f9a13e9e81e67de8e108a0a84ba2d463a503fad9ff58
|
|
| MD5 |
154dc45b25de89845c5cfa12f1dfc631
|
|
| BLAKE2b-256 |
3d489b203badeb64e3166dc2708169ad8a41dbb84a3a826887291ca0d98e0048
|
Provenance
The following attestation bundles were made for mashpy-0.19.0.tar.gz:
Publisher:
release-please.yml on imsid/mashpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mashpy-0.19.0.tar.gz -
Subject digest:
5331514fd38d4b2715f5f9a13e9e81e67de8e108a0a84ba2d463a503fad9ff58 - Sigstore transparency entry: 2255317285
- Sigstore integration time:
-
Permalink:
imsid/mashpy@d4b5f89b9e7399d0b17e28628f0c5b2e7face798 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/imsid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@d4b5f89b9e7399d0b17e28628f0c5b2e7face798 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mashpy-0.19.0-py3-none-any.whl.
File metadata
- Download URL: mashpy-0.19.0-py3-none-any.whl
- Upload date:
- Size: 475.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5fb6a3544ec5824749bb9e414df0d18766117398c3c3f76627f6862ca45aebe
|
|
| MD5 |
a028d47b7459d4b150cea25e77098c37
|
|
| BLAKE2b-256 |
430590c89798e5e00ca1c3d915451a3632c76d1d92e8bc245234ba20f0c997ac
|
Provenance
The following attestation bundles were made for mashpy-0.19.0-py3-none-any.whl:
Publisher:
release-please.yml on imsid/mashpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mashpy-0.19.0-py3-none-any.whl -
Subject digest:
b5fb6a3544ec5824749bb9e414df0d18766117398c3c3f76627f6862ca45aebe - Sigstore transparency entry: 2255317325
- Sigstore integration time:
-
Permalink:
imsid/mashpy@d4b5f89b9e7399d0b17e28628f0c5b2e7face798 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/imsid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@d4b5f89b9e7399d0b17e28628f0c5b2e7face798 -
Trigger Event:
push
-
Statement type: