Install
$ agentstack add mcp-metravod-zymi-core Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
zymi-core
The auditable MCP backend for agents — tools as declarative YAML pipelines: event-sourced, replayable, approval-gated.
Pronounced zoomi — like dog zoomies.
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](#zymi-as-an-mcp-server--pipelines-as-tools-for-any-agent) 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-observabilityand 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](#run-a-telegram-agent-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](adr/0032-install-ux-fetch.md)). > 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_pollconnector — long-polls Telegram'sgetUpdates, no HTTPS / ngrok neededhttp_postoutput — sends eachResponseReadyback to the user- Telegram approval channel — DMs admins with ✅ / ❌ buttons when the agent calls
broadcast(requires_approval: true) - Python
@toolauto-discovery — droptools/get_weather.py(sync) ortools/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](docs/getting-started.md). Connector deep-dive in [docs/connectors.md](docs/connectors.md). Approvals in [docs/approvals.md](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](adr/0024-deterministic-tool-steps.md)) — direct dispatch with templated args, no LLM hop, but the same event envelope — or an ask step ([ADR-0042](adr/0042-mcp-sampling-ask-step.md)): 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](adr/0028-conditional-dag-edges.md)) — 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](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/.yml— no code. - Python
@toolintools/.py— sync or async, signature → JSON Schema, auto-discovered. - MCP servers — one
mcp_servers:entry gives N tools, namespacedmcp____([ADR-0023](adr/0023-mcp-client-integration.md)). - Builtins —
read_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](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](adr/0033-mcp-server-pipelines-as-tools.md)).
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'
- Sync —
tools/callblocks until the pipeline finishes; works on every MCP client today. Tool input schema is auto-generated from the pipeline'sinputs:. - Async — a client that augments the call with a SEP-1686 task gets a
CreateTaskResultimmediately and pollstasks/get/tasks/result/tasks/list;tasks/cancelandnotifications/cancelledcancel 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](#approvals--event-sourced-restart-safe) 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](docs/pipelines.md#ask-step-adr-0042) ([ADR-0042](adr/0042-mcp-sampling-ask-step.md)) 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](adr/0034-mcp-observability-tools.md)). 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 taskinput_required+ resume surface, so anask:inside an async task is answered viazymi/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 serveis Unix-only for now (stdio); tasks live for the server process lifetime (no TTL eviction). Hosts may normalise dotted tool names — Claude Code showszymi.runs.listaszymi_runs_list.
Design, wire shapes, and the approval bridge → [ADR-0033](adr/0033-mcp-server-pipelines-as-tools.md).
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](docs/connectors.md#http-poll)):
# 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](adr/0022-event-sourced-approvals.md)).
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](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](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](adr/0012-cross-process-event-delivery.md)). Same hash-chain semantics either way. → [docs/store-backends.md](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](docs/context.md) for recommended chat / coding / evals profiles ([ADR-0016](adr/0016-context-window-management.md)).
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
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [metravod](https://github.com/metravod)
- **Source:** [metravod/zymi-core](https://github.com/metravod/zymi-core)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.