Skip to main content

zymi

zymi-core

The auditable MCP backend for agents — tools as declarative YAML pipelines: event-sourced, replayable, approval-gated.

Pronounced zoomi — like dog zoomies.

PyPI Python versions CI License: MIT llms.txt


Why zymi-core?

Agent frameworks compete for the front of the stack — the loop, the planner, the IDE. zymi owns the back: the tools your agent calls.

zymi mcp serve exposes declarative YAML pipelines as MCP tools to any host — Claude Code, Claude Desktop, Cursor, or any framework with an MCP adapter (LangGraph, CrewAI, OpenAI Agents SDK). Unlike a script behind an endpoint, a zymi tool is:

  • Declarative, like dbt. Agents, pipelines, tools, connectors, approvals — all YAML. The engine validates and runs them as a DAG.
  • Event-sourced. Every state change is an immutable, hash-chained event. Runs are replayable, resumable, and auditable without extra logging.
  • Boundary-safe — interactively. Steps emit intentions (run shell, write file, call HTTP) that pass through policy + contracts + optional human approval before execution. Over MCP the approval renders as an approve/deny form right in the calling agent's UI; the risky thing doesn't happen until someone says yes.
  • Self-debuggable. Serve with --expose-observability and the agent can introspect its own runs — list them, pull the event trace, read any step's exact I/O — and explain a failure without you opening a log file.

zymi is deliberately not an autonomous coding agent, an IDE plugin, or a chat UI — it's the governed tool layer underneath those. It also runs standalone: bring a Telegram agent online in two minutes, no MCP involved. Either way, a year later you can still answer exactly what this agent did on any past run.

📚 AI-assistant friendly out of the box. Every zymi init scaffold drops an AGENTS.md into the user's project — vocabulary, file map, task→file routing. Claude Code / Cursor / Aider read it automatically; the YAML they help you write gets noticeably more correct. For agents that build zymi projects (rather than work inside one), install zymi-skill into your assistant — opinionated Agent Skill with activation rules + progressive disclosure references, so the assistant produces zymi-native YAML instead of generic agent advice.


Run a Telegram agent in two minutes

The canonical standalone demo (no MCP host needed) — a real chat bot, wired declaratively.

uv tool install zymi-core    # one-time; puts `zymi` on PATH globally

mkdir telegram-agent && cd telegram-agent
zymi init --example telegram

# 1. Create a bot via @BotFather in Telegram; copy the token.
# 2. Fill .env:
cp .env.example .env         # edit TELEGRAM_BOT_TOKEN + OPENAI_API_KEY
# 3. Open project.yml, replace "your_username_here" with your actual
#    Telegram username (no @). Keeps strangers out of the bot.

zymi fetch                   # uv sync — builds ./.venv from pyproject.toml
zymi serve chat              # .env is auto-loaded; pipeline runs in ./.venv

Why uv tool install and zymi fetch? zymi is a global CLI; your project keeps its own pyproject.toml + .venv for any Python deps your @tool files import. zymi fetch wraps uv sync to build that venv, and pipeline-run commands transparently re-exec inside it (ADR-0032). Don't have uv yet? curl -LsSf https://astral.sh/uv/install.sh | sh (macOS/Linux) or irm https://astral.sh/uv/install.ps1 | iex (Windows).

Message the bot. It replies in seconds. Every inbound message, LLM call, approval decision, and outbound reply is in .zymi/events.db; watch live with zymi observe.

The whole wiring — Telegram I/O, two-step DAG (assistant drafts, reviewer polishes), declarative + Python tools, approval channel — lives in YAML. The scaffold also drops AGENTS.md so an AI coding assistant can extend the project safely. Concrete demo of:

  • http_poll connector — long-polls Telegram's getUpdates, no HTTPS / ngrok needed
  • http_post output — sends each ResponseReady back to the user
  • Telegram approval channel — DMs admins with ✅ / ❌ buttons when the agent calls broadcast (requires_approval: true)
  • Python @tool auto-discovery — drop tools/get_weather.py (sync) or tools/translate.py (async) and the agent picks them up

Ask the bot to "announce that we're closing at 5pm" — the agent calls broadcast, you get a DM with approve/deny buttons, nothing goes out until you click. End-to-end audit trail in zymi events.

Full setup in docs/getting-started.md. Connector deep-dive in docs/connectors.md. Approvals in docs/approvals.md.


What's in the box

Pipelines — DAGs, agent steps, deterministic tool steps, ask steps

A pipeline is a list of steps with depends_on: edges. Independent steps run in parallel. Each step is an agent step (LLM ReAct loop), a deterministic tool step (ADR-0024) — direct dispatch with templated args, no LLM hop, but the same event envelope — or an ask step (ADR-0042): delegate a reasoning question to whoever called the pipeline instead of configuring a second, separately-billed model. The run parks, asks the caller, and resumes with the answer (a human at the terminal under zymi run; the connected agent under zymi mcp serve).

Mix them freely:

steps:
  - id: fetch                            # deterministic — no LLM
    tool: http_get
    args: { url: "https://api.example.com/${inputs.id}" }

  - id: classify                         # LLM
    agent: classifier
    task: "${steps.fetch.output}"
    depends_on: [fetch]

  - id: sanity                           # ask — the caller answers, no llm: needed
    ask: "Does this classification look right?\n${steps.classify.output}"
    depends_on: [classify]

Conditional branches (ADR-0028) — a step can gate on an upstream output. Skipped branches cascade to descendants and emit StepSkipped events, so routing decisions land in the trace, not in the LLM's head:

- id: router
  agent: concierge
  task: "Pick: ${inputs.q}"   # calls route('short' | 'rag')

- id: rag_lookup
  tool: pinecone_query
  args: { query: "${inputs.q}" }
  depends_on: [router]
  when: "${steps.router.output} == 'rag'"

Schema, examples, gotchas → docs/pipelines.md.

Tools — four kinds, one catalogue

All four kinds emit identical ToolCallRequested / ToolCallCompleted events; the agent doesn't know which catalogue a tool came from.

  • Declarative HTTP / shell in tools/<name>.yml — no code.
  • Python @tool in tools/<name>.py — sync or async, signature → JSON Schema, auto-discovered.
  • MCP servers — one mcp_servers: entry gives N tools, namespaced mcp__<server>__<tool> (ADR-0023).
  • Builtinsread_file, write_file, write_memory, execute_shell_command, spawn_sub_agent.
# tools/get_weather.py — auto-discovered at runtime startup.
from zymi import tool

@tool
def get_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"sunny in {city}"

Schema and the four kinds in detail → docs/tools.md.

zymi as an MCP server — pipelines as tools for any agent

The mirror of the MCP client above: zymi mcp serve exposes your pipelines as MCP tools over stdio, so any MCP host (Claude Code, Claude Desktop, Cursor, the OpenAI Agents / LangGraph / OpenHands runtimes via their MCP adapters) can call a zymi pipeline as a single tool — no per-runtime glue (ADR-0033).

This is the priority direction for zymi: own the auditable, event-sourced back of the agent stack rather than competing on the front. A pipeline is a tool whose every step is hash-chained, replayable, and resumable — which is exactly what an agent's tool catalogue is missing.

Exposure is opt-in per pipeline (so internal/cron pipelines never leak into agent tool catalogues):

# pipelines/research.yml
expose:
  mcp:
    name: research            # tool name (defaults to file stem)
    mode: sync | async        # async hints the caller to task-augment (SEP-1686)
    description: "Deep-research a topic and return a brief."
zymi mcp serve                              # serve all expose:-d pipelines over stdio
zymi mcp serve --include 'research_*' --exclude '*_internal'
  • Synctools/call blocks until the pipeline finishes; works on every MCP client today. Tool input schema is auto-generated from the pipeline's inputs:.
  • Async — a client that augments the call with a SEP-1686 task gets a CreateTaskResult immediately and polls tasks/get / tasks/result / tasks/list; tasks/cancel and notifications/cancelled cancel it. The pipeline runs in the background and stays fully observable in the event store.

Human approvals render in the calling agent's UI. A pipeline step that trips an approval sends a server-initiated elicitation/create back through the live tools/call — in Claude Code that's a native approve/deny form. Approve and the pipeline continues; deny and it halts with the decision in the audit trail; a client without elicitation support fail-closes (ApprovalDenied{reason: client_no_elicitation}). Verified live against Claude Code.

The pipeline can borrow the caller's brain. An ask: step (ADR-0042) delegates a reasoning question back to the calling agent instead of configuring a second model. On a task-augmented call the run parks, the task goes input_required carrying { prompt, resume_token }, and the caller reasons in its own loop and calls zymi/reasoning/resume { resume_token, answer } — no sampling, no deprecated primitives, just tools + park/resume. The prompt and answer are recorded, so replay reads the answer back byte-identical. A pure tool + ask pipeline needs no llm: at all. Verified live against zymi mcp serve.

The agent can debug its own runs. zymi mcp serve --expose-observability adds four read-only tools — zymi.runs.list / .get / .events / .step_io (ADR-0034). Ask the agent "why did the last run fail?" and it pulls the event trace and answers with the exact policy verdict and approval decision — introspection other stacks can't expose because the per-step event granularity isn't there. Scoped to the serve session by default; --observability-scope all opens the whole store for single-user dev.

Current limitations (honest list):

  • Async tasks don't pause for approvals. The interactive approval (elicitation) bridge above is sync-mode; an approval inside a task-augmented call waits on host adoption and times out (auto-deny). Note this is specific to approvals — reasoning delegation (ask: steps, ADR-0042) works precisely because it rides the task input_required + resume surface, so an ask: inside an async task is answered via zymi/reasoning/resume. Sync calls are fully interactive for both.
  • Cancellation is best-effort: the task is aborted, but pipeline steps already in flight (and their side effects) may run to completion.
  • Arguments cross the boundary as strings — pipelines expecting string inputs: are fine; richly typed inputs are stringified.
  • Async mode needs a SEP-1686-capable client; zymi mcp serve is Unix-only for now (stdio); tasks live for the server process lifetime (no TTL eviction). Hosts may normalise dotted tool names — Claude Code shows zymi.runs.list as zymi_runs_list.

Design, wire shapes, and the approval bridge → ADR-0033.

Connectors and outputs

Inbound: http_inbound (webhook), http_poll (long-poll), cron, file_read, stdin. Outbound: http_post, file_append, stdout.

All declarative, all emit events. Filter recipes (docs/connectors.md):

# GitHub — only react to PR opens
filter:
  "$.action":              { equals: "opened" }
  "$.pull_request.draft":  { equals: false }

429 + Retry-After handled automatically. Cursors persist across restarts. Multi-process zymi serve against shared Postgres sees one cursor table, no double-fire.

Approvals — event-sourced, restart-safe

Tools with requires_approval: true publish ApprovalRequested on the bus; an approval channel routes a human decision back. Four channels in the box: terminal, http, telegram, and mcp_elicitation — the default under zymi mcp serve, rendering the approve/deny form in the calling MCP host (ADR-0022).

Resolution order: pipeline override → project default → fail-closed. A zymi serve crash mid-approval is repaired on next start: in-flight requests are redelivered to live channels; expired ones are sealed with ApprovalDenied{reason: restart_timeout}.

Full schemas + telegram setup → docs/approvals.md.

Replay, resume, observe

zymi runs                                   # all pipeline runs
zymi events --stream pipeline-chat-abc      # every event in one run
zymi verify --stream pipeline-chat-abc      # hash-chain integrity check
zymi observe                                # 3-panel TUI: runs / DAG / events live

# Fork-resume from a chosen step. Upstream steps are frozen; the fork
# step + DAG-descendants re-run against current configs on disk.
zymi resume pipeline-chat-abc --from-step polish
zymi resume pipeline-chat-abc --from-step polish --dry-run

Useful when you're iterating on a prompt: don't re-burn the expensive early steps every time you tweak the later ones. → docs/events-and-replay.md.

Store backends

SQLite (default, zero-config) for single-process / dev. Postgres for multi-process zymi serve against shared state — one store: postgres://… line in project.yml (ADR-0012). Same hash-chain semantics either way. → docs/store-backends.md.

Context window management

The agent's working context is reconstructed from the event log each iteration, not accumulated in a buffer. Older tool observations are masked in-place (~2× cost reduction, no extra LLM calls). When the budget still gets tight, hybrid compaction summarises the oldest masked batch with one fast LLM call. Tunable in runtime.context: — see docs/context.md for recommended chat / coding / evals profiles (ADR-0016).

JSON Schemas for configs

IDE autocomplete and LLM-assisted YAML come free:

zymi schema project          # draft-07 JSON Schema for project.yml
zymi schema --all

Python embedding

When zymi-core is in your project's venv (uv add zymi-core in a uv project, or pip install zymi-core in a traditional venv), the same wheel exposes a Python API: Runtime, Event, EventBus, EventStore, Subscription, ToolRegistry, plus the @tool decorator.

from zymi import Runtime

rt = Runtime.for_project(".", approval="terminal")
result = rt.run_pipeline("chat", {"message": "hello"})
print(result.success, result.final_output)

rt.bus() and rt.store() share Arc-handles with the runtime — Python subscribers see exactly what the handler publishes.

Cross-process pattern (Django view / Celery task drives zymi serve over the shared store):

import uuid
from zymi import Event, EventBus, EventStore

store = EventStore(".zymi/events.db")
bus = EventBus(store)

corr = str(uuid.uuid4())
sub = bus.subscribe_correlation(corr)

ev = Event(
    stream_id=f"web-{corr}",
    kind={"type": "PipelineRequested",
          "data": {"pipeline": "research", "inputs": {"topic": "rust event sourcing"}}},
    source="django",
)
ev.with_correlation(corr)
bus.publish(ev)

result = sub.recv(timeout_secs=300)

Full surface → docs/python-api.md.


CLI cheatsheet

zymi init [--example telegram]              # scaffold a project (writes pyproject.toml too)
zymi fetch                                  # uv sync — build ./.venv from pyproject.toml
zymi run <pipeline> -i key=value           # one-shot run (re-execs in ./.venv if present)
zymi serve <pipeline>                       # long-running: react to PipelineRequested

zymi runs                                   # list pipeline runs
zymi events [--stream ID] [--kind TAG]      # query event log
zymi verify [--stream ID]                   # hash-chain integrity check
zymi observe [--run ID]                     # interactive TUI
zymi resume <run-id> --from-step <id>       # fork-resume

zymi mcp probe <name> -- <cmd> [args ]     # smoke a third-party MCP server
zymi mcp serve [--expose-observability]     # serve expose:-d pipelines as MCP tools
              [--include G] [--exclude G]   #   + zymi.runs.* introspection tools
zymi schema {project|agent|pipeline|tool|--all}

Full reference → docs/cli.md.


Documentation


Contributing & License

zymi-core is built in Rust and shipped via PyPI. Bug reports, examples, PRs welcome — see CONTRIBUTING.md for the dev loop, test matrix, ADR workflow, and how to build from source.

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zymi_core-0.9.0.tar.gz (1.1 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

zymi_core-0.9.0-cp39-abi3-win_amd64.whl (6.4 MB view details)

Uploaded CPython 3.9+Windows x86-64

zymi_core-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.1 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

zymi_core-0.9.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.7 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

zymi_core-0.9.0-cp39-abi3-macosx_11_0_arm64.whl (5.5 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

zymi_core-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file zymi_core-0.9.0.tar.gz.

File metadata

  • Download URL: zymi_core-0.9.0.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zymi_core-0.9.0.tar.gz
Algorithm Hash digest
SHA256 c4d71a1f935d26eed661362cb313a7d277b5747a253e8ec3da2099ac8435c5f5
MD5 24011dfc53221c9fb92d690b39cfb46a
BLAKE2b-256 4aae86ca4982027bdc487c2a063531ec2426c61fa3660b0833119953e37266cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0.tar.gz:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zymi_core-0.9.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: zymi_core-0.9.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 6.4 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zymi_core-0.9.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 c25054f4e5fd4611be736af5f96b32bb4c672c74c437a2edccc5f10cf510deb4
MD5 0d24617027f6c68d18b622d518f6806b
BLAKE2b-256 b4419b31c03dbb7bb4e3067cc61b416e670b6be8edafc2a65e21b124512e18b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0-cp39-abi3-win_amd64.whl:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zymi_core-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for zymi_core-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e92c3d5beca5ef9151c1df47c94584e71f6dc9f7857f02c163cd7f98500c6458
MD5 c910062b8fb1f4c327d1f281af621a40
BLAKE2b-256 005a58fceb91d1e79a38a76316f8b158e15affd96747071096c3474674b7d37c

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zymi_core-0.9.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for zymi_core-0.9.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e6c375b7aeebc19d92ff3c492b4e25af413ad7e4726b0cc072c3e292bd0c041f
MD5 6bb81c2cda8b590146bc7f9984b97a89
BLAKE2b-256 56879d3d0bc9af9a3e21ac1b6509c4648052d233e9be47a434a0b20ec16ac686

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zymi_core-0.9.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for zymi_core-0.9.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4df3a6b68a1e7168dddd6b054fba03c44a52532adfd7e4e5bb6dc2e877f88167
MD5 88856ab45d311aad7ef04a1d5cbcfc56
BLAKE2b-256 f237ccb5fd14156f356a55d7c374c9290cb93d09d47727a8f1ee7517993f15fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zymi_core-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for zymi_core-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 7360287d0d3815fb4dc99c2fc92e1faa008acd12149e32382a9edf0fafb37b5a
MD5 4d1f20b895b585bfd33570358c06a2e7
BLAKE2b-256 5f51a176f07a1bb23a9fb36b9c8e33d84457c79d7553a5261974703426a3b61c

See more details on using hashes here.

Provenance

The following attestation bundles were made for zymi_core-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on metravod/zymi-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.9.0 This release

6 files

0.8.1

6 files

0.8.0

6 files

0.7.3

6 files

0.7.2

6 files

0.7.1

6 files

0.7.0

6 files

0.6.13

6 files

0.6.12

6 files

0.6.11

6 files

0.6.10

6 files

0.6.9

6 files

0.6.8

6 files

0.6.7

6 files

0.6.6

6 files

0.6.5

6 files

0.6.4

6 files

0.6.3

6 files

0.6.2

6 files

0.6.1

6 files

0.6.0

6 files

0.5.1

6 files

0.5.0

6 files

0.4.0

6 files

0.3.0

6 files

0.2.2

6 files

0.2.1

6 files

0.2.0

6 files

0.1.8

6 files

0.1.7

6 files

0.1.6

6 files

0.1.5

6 files

0.1.4

6 files

0.1.3

6 files

0.1.2

6 files

0.1.1

6 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page