Skip to main content
Glama
README.md
# MCP Moira

Agent Workflow Engine for AI agents.

**Primary users:** AI agents via MCP protocol. Web UI is supplementary for workflow management.

See [docs/VISION.md](docs/VISION.md) for product vision and design principles.

## Two ways to run Moira

- **Self-host (this repository, Apache-2.0)** — run the full engine + Web UI + MCP
  server and its ingress through Docker Compose on your own infrastructure. Free and open
  source; your data stays with you. Start in the [Quick Start](#self-host-recommended)
  below.
- **Moira Cloud (managed)** — a hosted instance with nothing to operate, at
  [moira-mcp.com](https://moira-mcp.com).

Both run the **same engine and MCP tools**. Self-host is a single-tenant
private-team deployment with administrator-approved accounts. Cloud adds managed
hosting and SaaS-only policy and administration, including social login, legal
consent, email verification, and the broader multi-user administration surface.

## Architecture

**Monorepo**: Clean separation of concerns with npm workspaces
**Workflow Engine**: Node-graph execution over a set of node types (@mcp-moira/workflow-engine)
**MCP Server**: HTTP protocol server exposing the MCP tools (@mcp-moira/mcp-server)
**Web Backend**: Express API server (@mcp-moira/web-backend)
**Web Frontend**: React UI with webpack (@mcp-moira/web-frontend)
**Docs**: Astro 5 + Starlight documentation site, EN+RU (@mcp-moira/docs)
**Shared**: Database layer + Better Auth + logging (@mcp-moira/shared)
**Database**: Modular repository pattern with Drizzle ORM
**Settings System**: Universal settings with encryption and dynamic UI generation
**Docker Deployment**: Multi-stage container with TypeScript validation
**Validation**: JSON Schema with AJV

### Package Structure

- **packages/workflow-engine/** - Core node-graph execution engine
- **packages/extension-sdk/** - Typed authoring contract for custom nodes and communication channels
- **packages/extension-runner/** - Isolated companion service for installed extension bundles
- **packages/local/** - Paired controller for workspace provisioning and optional Docker Sandboxes microVMs
- **packages/workspace-service/** - Common guest command, file and session service with its Unix-socket CLI
- **packages/mcp-server/** - MCP protocol HTTP server with tools
- **packages/web-backend/** - Express API for workflow management
- **packages/web-frontend/** - React UI for workflow visualization
- **packages/docs/** - Astro 5 + Starlight documentation site (EN+RU), built into the image and served at `/docs`
- **packages/shared/** - Database layer (schema, connection, repositories) + Better Auth + logging
  - `database/` - Modular repositories (Workflow, Execution, Settings)
  - `auth/` - Better Auth configuration
  - `logging/` - Structured logging
- **Docker Config** (`config/`) - Unified container deployment configuration

## Quick Start

### Self-Host (recommended)

Run a complete Moira instance locally with Docker — no source build required:

```bash
cp .env.example .env       # defaults work locally; review host, port, and artifact domain for another host
docker compose up -d
```

Then open:

- **Web UI**: http://localhost:8080
- **Documentation**: http://localhost:8080/docs/
- **MCP endpoint**: http://localhost:8080/mcp

Compose starts the application and static Traefik ingress on the same public port. Keep
`config/traefik-self-host.yml` with the Compose file; the [Self-Hosting guide](packages/mcp-server/src/help/content/getting-started/self-hosting.md)
includes the three-file download procedure. The ingress forwards `/api/workspaces/` HTTP/1.1 Upgrade
directly to backend port 3001 and ordinary traffic to nginx 80, without Docker socket access.
Its application `TRUST_PROXY` default is 2; configure explicit trusted proxy addresses for another
ingress topology. A standalone frontend-port mapping is not the supported workspace stream route.

The image is pulled from the public registry by default. Cloud account, workflow and execution
data persists in `./data`. Workspace files, operation results and immutable file publications stay
in the workspace service or paired controller's private storage. Back up those owner stores with
their VM disks; a cloud database backup does not contain their file bytes.
See [Self-Hosting](#self-host-recommended) or the in-app docs
at `/docs/` for the full reference.

### Updating / Upgrading

Update a normal self-host installation with the standard Compose commands:

```bash
docker compose pull
docker compose up -d
docker compose ps
```

Older `.env` files may still override Compose with the removed `0.3.5` tag; change that line once to
`MOIRA_IMAGE=ghcr.io/moira-mcp/moira:latest` before updating.

The image protects existing self-host data before its startup migrations: it creates and verifies a
coherent SQLite backup, includes the prompt manifest, and keeps three rotating recovery states under
`data/.moira-startup-backups/`. If initialization fails, it restores the database and manifest before
refusing to start the services. A persistent pending marker also restores the verified state before the
next attempt if the container or host was interrupted mid-initialization. A fresh installation skips
the nonexistent-database backup and uses only a temporary persistent marker so an interrupted first
start is removed before retry.

The complete automatic recovery behavior and optional pinned-image preflight are documented in
[Self-hosting: Updating and Recovery](packages/docs/src/content/docs/docs/getting-started/self-hosting.mdx#updating-and-recovery), with a matching [Russian version](packages/docs/src/content/docs/ru/docs/getting-started/self-hosting.mdx#обновление-и-восстановление). Release notes are on the [GitHub Releases](https://github.com/moira-mcp/moira/releases) page.

### Local Development (from source)

For contributors who want to build and run from the source tree, switch
`docker-compose.yml` to **Option B** first — comment out the `image:` line and
uncomment the `build:` block (the file documents both options inline). Then:

```bash
nvm use
npm install
docker compose up -d --build   # builds the image locally from config/Dockerfile
# Web UI: http://localhost:8080  |  MCP: http://localhost:8080/mcp
```

(The default `docker-compose.yml` uses the prebuilt public image — `docker compose
up -d` without `--build` — which is the recommended self-host path.)

### Extensions (optional)

Custom node and ordinary text-channel handlers run in the separate extension runner and are disabled by default. File-capable channels additionally declare a fixed `binaryTransport` and run their streaming `binaryHandler` beside the source at its workspace or paired controller. Enabling extensions
requires this source checkout because the runner image is built locally; the published Moira image
does not import or package bundle code.

```bash
mkdir -p extensions
cp -R examples/extensions/webhook-notify extensions/
printf '\nMOIRA_EXTENSION_RUNNER_URL=http://moira-extension-runner:9110\n' >> .env
docker compose --profile extensions up -d --build
```

Before starting the example, replace both placeholder network permissions and fill its per-user
settings. The example contributes both a workflow action node and an ordinary notification channel.
See [Self-hosting: Enable
extensions](packages/docs/src/content/docs/docs/getting-started/self-hosting.mdx#enable-extensions)
and [Writing an
Extension](packages/docs/src/content/docs/docs/guides/writing-extensions.mdx) for the complete
installation, SDK, permission and failure contracts. `npm run test:docker-extensions` verifies the
default-off profile and read-only bundle mount.

### Local codespaces on your computer

Moira Local connects a paired controller to your Moira account through outbound HTTPS. The controller
can provision the common service in GitHub Codespaces and can run locally approved repositories in
persistent, mountless Docker Sandboxes microVMs. Enrollment and controller startup require no Local
VM initialization or Docker sign-in. Public read-only Local work needs no GitHub App connection;
private Git and write operations use repository-scoped credentials derived from the account's
server-held GitHub authorization.

The controller requires Node.js 24. Its optional physical Local VM capability requires macOS on
Apple silicon and the pinned Docker Sandboxes `sbx` 0.46.0 runtime; Linux Local VM execution is
refused. From this source checkout:

```bash
npm ci
npm run local:build
npm run local -- --help
```

The package is private and is not a published global npm installer. Pair the controller through
**Settings → Development → Local computers**; the pairing token reaches the companion through stdin.
For Local VM work, initialize the physical capability and approve repository rights and a finite
work lease on that computer. Review effective owner grants in the browser before remote work.
See [Codespaces: Moira Local](docs/CODESPACES.md#moira-local-companion)
for setup, run, stop, recovery and the limits of isolation evidence. Codespace contents are visible
to Moira; review returned code before executing it on your computer. The `moira-local` MCP connection
name in the examples below is independent of the companion executable.

The same workspace service owns commands, files, sessions and output in Local and GitHub guests.
The Local owner prepares its guest; the paired controller performs fixed GitHub provisioning or
explicit in-place adoption. The service includes its own Node.js 24.21.0 and ripgrep 15.2.0, independent of
project Node/PATH. Inside a prepared workspace, `~/.local/bin/moira-workspace --help` exposes the
direct Unix-socket CLI. Choose `ws1-<lowercase UUID>` for command and mutation requests; plain UUIDs
remain repository-admission identities. Inspect the original identity after a lost reply before
repeating an effect. Command output and file bytes stream without Moira byte/concurrency/runtime
quotas; explicit observation/deadline options, real storage capacity and repository/owner guards
remain. A ready published attachment can be sent through
the separately requested `communication` action `deliver-file`. See
[File operations and native transfer](docs/CODESPACES.md#file-operations-and-native-transfer)
for current authority, link expiry and guarded target replacement. The common OCI image is
`ghcr.io/moira-mcp/moira-workspace-service`; a project keeping its own Linux Dev Container can use
the optional `ghcr.io/moira-mcp/features/workspace-service` Feature with a published release tag.
Host `moira-local create`, `start`, `stop` and `remove` call the running owner controller with the
observed generation and an original request ID; `collect` inspects that ID without repeating work.
Creation also needs cloud route registration and initial Git authority. Existing owner-admitted
Local work and its guest CLI remain subject to current local grants and leases during a temporary
cloud outage. A confirmed authority refusal does not grant offline access. Raw host `request` and
credential-export commands are unsupported; `recover` remains an explicit custody action.

For GitHub, a repository without the workspace Feature needs an online assigned controller for
fixed installation. An existing environment uses explicit adoption without replacement; a checked-in
Feature is an opt-in startup path. Guest readiness requires authenticated outbound registration.
A definitive registration refusal fails startup, while temporary network loss reconnects observation
without replaying commands or lifecycle operations.

Workflow materialization resolves authorized text in Moira and creates its UTF-8 tar at a selected
paired controller. The download grant pins that controller and original publication request; repeated
reads collect the same immutable archive without rendering again or choosing another owner. If the
first publication is unconfirmed, collect that grant or request a fresh current-step presentation for
an explicit retry. The in-context `session materialize` fallback requires no file owner and retains
its separate response-size budget.

### Testing

The integration/API/E2E suites run against a local Docker container, configured by
`.env.local`. Copy the template once before running them (or before
`npm run docker:restart`):

```bash
cp .env.local.example .env.local   # then set BETTER_AUTH_SECRET
npm test              # All tests
npm run test:unit     # Unit tests only (no container needed)
npm run test:e2e      # E2E tests
```

### Code Quality

```bash
npm run fix                   # ESLint + Prettier fix all files
```

**Configuration in `.env`** (copy from `.env.example`):

- MOIRA_PORT: External access port (default 8080)
- MOIRA_HOST: Public host:port the instance is served on (default localhost:8080)
- STATIC_ARTIFACTS_DOMAIN: Wildcard subdomain base for published artifacts
- BETTER_AUTH_SECRET: generated and persisted on first start when empty; set it
  explicitly only when you want to manage the auth signing secret yourself
- Database: SQLite at `./data/moira.db` (bind-mounted, persists across restarts)
- Admin: ADMIN_EMAIL, ADMIN_PASSWORD (auto-generated on first start if unset)

## Authentication

MCP Moira uses Better Auth with OAuth 2.1 for centralized authentication.

**Browser Access:**

- Email/password login at http://localhost:8080/login
- GitHub/Google OAuth (saas mode only; disabled in self-host)
- Better Auth UI components (Tailwind + shadcn/ui)

**MCP Clients:**

- OAuth 2.1 authorization code flow
- HTTP 401 triggers OAuth discovery
- Dynamic Client Registration (DCR) supported
- Access token required for all MCP tool calls

**Protected:**

- All MCP tools require authentication
- Account API routes require authenticated access; public capability routes enforce their own
  narrower grants
- The guest workspace's outbound connection receives authority for that resource and measured
  environment only; it receives no full MCP, GitHub or communication credential
- Shared middleware and service guards enforce the required account and resource authority

**Testing:**

```bash
docker compose up -d
# Access:        http://localhost:8080/login
# MCP Inspector: http://localhost:8080/mcp
```

See [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) for complete setup and OAuth flow details.

### MCP Configuration

Point your MCP client (e.g. Claude Code) at your running instance:

| Server        | URL                       | Purpose                       |
| ------------- | ------------------------- | ----------------------------- |
| `moira-local` | http://localhost:8080/mcp | Your local self-host instance |

```json
{
  "mcpServers": {
    "moira-local": { "url": "http://localhost:8080/mcp" }
  }
}
```

> Replace `localhost:8080` with your own host/port (`MOIRA_HOST`) if you serve Moira
> on a different address.

## Representative Node Examples

The examples below show common graph patterns; they are not the complete node-type inventory. See
the [Nodes reference](packages/docs/src/content/docs/docs/concepts/nodes.mdx) for every supported
type and its current contract, including automatic note operations and file materialization.

### Start Node

```json
{
  "type": "start",
  "id": "start",
  "connections": { "default": "next-node-id" }
}
```

### Agent Directive Node

```json
{
  "type": "agent-directive",
  "id": "task",
  "directive": "Task instruction",
  "completionCondition": "Success criteria",
  "inputSchema": {/* JSON Schema */},
  "connections": { "success": "next-node-id" }
}
```

### Condition Node

```json
{
  "type": "condition",
  "id": "check",
  "cases": [
    {
      "when": { "operator": "gte", "left": { "contextPath": "score" }, "right": 8 },
      "output": "passed"
    }
  ],
  "connections": {
    "passed": "success-path",
    "default": "failure-path"
  }
}
```

`cases` are evaluated in authored order; the first case whose `when` holds selects its `output`,
and `default` is taken when none holds.

### User Notification Node

```json
{
  "type": "user-notification",
  "id": "notify",
  "message": "Task completed: {{result}}",
  "format": "plain",
  "connections": { "default": "next-node-id" }
}
```

The node fans out to the current user's enabled communication channels and never accepts provider,
recipient, or credential fields. The deprecated `telegram-notification` node remains available for
existing Telegram-specific workflows, including exact explicit `chatId` semantics.

### End Node

```json
{
  "type": "end",
  "id": "end",
  "finalOutput": ["result", "score"]
}
```

### Expression Node

```json
{
  "type": "expression",
  "id": "increment-counter",
  "expressions": ["counter = counter + 1"],
  "connections": { "default": "next-step" }
}
```

### Teleport Node

```json
{
  "type": "teleport",
  "id": "teleport-replan",
  "directive": "Rewrite the development plan",
  "completionCondition": "New plan created",
  "hint": "Use when plan needs restructuring",
  "connections": { "success": "plan-node" }
}
```

### Subgraph Node

```json
{
  "type": "subgraph",
  "id": "run-subtask",
  "graphId": "subtask-workflow",
  "inputMapping": { "parentVar": "subVar" },
  "outputMapping": { "subResult": "parentResult" },
  "connections": { "success": "next-step" }
}
```

### Lock Node

```json
{
  "type": "lock",
  "id": "approval-gate",
  "reason": "Waiting for user approval before deployment",
  "connections": { "unlocked": "next-step" }
}
```

Pauses execution until explicitly unlocked. Sends PIN via Telegram with inline approve button. Unlockable via MCP tool, web UI, or Telegram callback.

## Workflow Format

```json
{
  "id": "workflow-id",
  "metadata": {
    "name": "Workflow Name",
    "version": "1.0.0",
    "description": "What this workflow does"
  },
  "nodes": [/* Node definitions */]
}
```

## Templates

Variables processed in `directive`, `completionCondition`, and `message` fields:

- `{{variable}}` - Context variable
- `{{nested.path}}` - Object property access
- `{{executionId}}` - System: current process ID
- `{{workflowId}}` - System: current workflow ID
- `{{runUrl}}` - System: the run's page in the web app

## MCP Tools

```bash
# Workflow Management
list
start {"workflowId": "workflow-id"}
step {"processId": "process-id", "input": "data"}
manage {"action": "create", "workflow": {...}}
manage {"action": "edit", "workflowId": "workflow-id", "changes": {...}}
manage {"action": "get", "workflowId": "workflow-id"}

# Session Information
session {"action": "user"}
session {"action": "executions"}
session {"action": "execution_context", "executionId": "execution-id"}
session {"action": "current_step", "executionId": "execution-id"}

# Execution Locking
lock {"action": "lock", "executionId": "execution-id", "reason": "Awaiting approval"}
lock {"action": "unlock", "executionId": "execution-id", "pin": "123456"}
lock {"action": "status", "executionId": "execution-id"}
lock {"action": "list"}

# User Settings
settings {"action": "get"}
settings {"action": "get", "category": "codespaces"}
settings {"action": "set", "key": "codespaces.idle_timeout_minutes", "value": 60}
settings {"action": "list"}

# Workflow Tokens
token {"action": "upload", "ttlMinutes": 60}
token {"action": "download", "workflowId": "workflow-id", "ttlMinutes": 60}

# User Communication
communication {"action": "send", "message": "The report is ready."}
communication {"action": "deliver-file", "request_id": "ws1-11111111-1111-4111-8111-111111111111", "referenceId": "<file.reference_id>", "message": "Report", "kind": "document"}
communication {"action": "operation", "request_id": "ws1-11111111-1111-4111-8111-111111111111", "device_id": "<device_id returned for that delivery>"}

# Documentation
help
help {"topic": "tools"}
help {"topic": "step"}
```

File delivery requires a live source owner and a caller-chosen original `request_id`.
Keep the returned owner identity and collect that same request after uncertainty; for a
workspace owner, use `codespace_id` instead of `device_id`. Collection never sends again.
Owner publications contain immutable bytes and support repeated reads until expiry under
current authority. Ordinary text, waiting reminders and trusted lock PIN delivery need no
file owner. Optional workflow progress images can be omitted while text sends independently;
an explicit file delivery cannot silently succeed as text. Progress-image mint/read reports
`COMMUNICATION_OWNER_UNAVAILABLE` with HTTP 503 when its paired controller is unavailable.

## File Structure

```
packages/workflow-engine/  # Core execution engine
packages/mcp-server/       # MCP HTTP server (internal port, behind nginx)
packages/web-backend/      # Express API (internal port, behind nginx)
packages/web-frontend/     # React UI (static build served by nginx)
data/                      # SQLite database (moira.db)
workflows/                 # Bundled public workflow catalog
docs/                      # Technical documentation
```

## Development

All development happens through Docker containers.

```bash
docker compose up -d --build  # Build and run the container
npm test                      # Run all tests
npm run fix                   # ESLint + Prettier fix
```

**Database**: SQLite at DB_PATH (default: ./data/moira.db)
**Migrations**: Drizzle ORM (`npx tsx scripts/run-migrations.ts`)
**Storage**: Workflows and executions in database with user isolation

## Documentation

**User Documentation**: Served by your running instance at `/docs/` (EN) and `/ru/docs/` (RU), built from `packages/docs` (Starlight).

**Technical Documentation**: `/docs` directory - system reference, API specs, development guides.

**[Project Checklist](docs/PROJECT_CHECKLIST.md)** - mandatory pre-commit checks executed by development workflows.

## Claude Code Commands

Custom slash commands in `/commands` directory. See [commands/README.md](commands/README.md) for installation and usage.

## HTTP Transport

### Architecture

- **Stateless Mode**: Each HTTP request creates new transport, no session storage
- **JSON-RPC 2.0**: MCP protocol over HTTP with proper error handling
- **Direct Tools**: MCP tools integrated in single process
- **Environment Inheritance**: HTTP server environment variables passed to tools

### Endpoints

```http
POST /mcp     # JSON-RPC requests (tools calls)
GET  /health  # Server health check
```

### Environment Variables

```bash
# Required for Telegram integration
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_DEFAULT_CHAT_ID=your_chat_id

# Optional
MCP_PORT=4202  # Internal MCP server port (accessed via nginx proxy)
LOG_LEVEL=info
DEBUG_CONSOLE=true  # Console logging for development
WORKFLOWS_DIR=./packages/web-backend/workflows/production
```

## Configuration

MCP server configuration (see [MCP Configuration](#mcp-configuration) for details):

```json
{
  "mcpServers": {
    "moira-local": {
      "url": "http://localhost:8080/mcp",
      "type": "http"
    }
  }
}
```

Environment variables passed via HTTP headers (recommended for HTTP transport).

Alternative: Set environment variables in your `.env` file.

## Code Quality

### ESLint + Prettier

Project uses ESLint with TypeScript support and Prettier for code formatting.

```bash
npm run fix   # Auto-fix lint errors and format code
```

**Pre-commit Hook:**
Husky pre-commit hook automatically runs ESLint and Prettier on staged files.

**Configuration:**

- `.eslintrc.json` - ESLint rules (strict for production code, relaxed for tests)
- `.prettierrc` - Prettier formatting rules
- Production code: `any` types are errors, must be properly typed
- Test code: `any` types allowed for flexibility

## Security

### Rate Limiting

Protection against spam and DoS attacks with tiered limits:

- **Ordinary API routes**: 100 requests/minute
- **Auth routes** (`/api/auth/*`): 1000 requests/minute
- **MCP endpoint** (`/mcp`): 1000 requests/minute
- **Workspace channel**: resource-scoped authorization is checked separately; binary streams do not
  pass through the ordinary JSON API body parser or its rate-limit queue

Exceeded limits return HTTP 429 Too Many Requests.

### Data Size Limits

Protection against oversized payloads:

- **Workflow JSON**: max 5MB
- **Execution context**: max 10MB

Exceeded limits return HTTP 413 Payload Too Large.

### GeoIP Logging

Request logging includes country detection via geoip-lite:

```json
{
  "method": "POST",
  "path": "/api/workflows",
  "ip": "203.0.113.1",
  "country": "US",
  "duration": 45,
  "status": 200
}
```

## Admin Features

### Administrator Overview

The `/admin` page shows deployment-neutral system health and managed-workflow reconciliation.
The Cloud `adminAnalytics` capability also enables recently active people, registrations,
executions needing attention, popular flows and execution analytics. Each card has its own period.
Activity means a recorded action or refreshed unexpired session, not continuous online presence.

Analytics excludes current administrators by default. Individual exclusions and an explicit
include-everyone choice are saved separately for each account in the current browser. Management
lists and system health remain outside these exclusions; default self-host does not expose these
installation-wide analytics.

### User Management

Admin panel at `/admin/users` provides:

- **User list** with approval, email verification, and blocked status
- **Work context** with recorded activity, accepted-step time, run count and most-used flows;
  unknown activity or step time is identified explicitly
- **User details** page with sessions, OAuth connections, email history
- **Approve** pending self-host registrations
- **Session management** - revoke individual sessions or all sessions
- **OAuth management** - revoke tokens by provider or all OAuth connections
- **Block/Unblock** users with reason
- **Send verification email** manually
- **Send password reset email** manually
- **Set a temporary password** for an ordinary user when email delivery is unavailable

### Execution Monitoring

The Cloud `multiUserAdmin` capability enables the cross-user panel at `/admin/executions`. It is
server-denied and hidden by the default self-host policy:

- View all user executions
- Filter by user, status
- Search by execution ID, workflow ID or note
- Read owner, note, lock/refusal badges, known start time and the last accepted step separately;
  a record edit does not change step time, and missing timestamps are not fabricated
- Open permitted run details, including recorded progress, locks and errors

### Email History

Track every email attempt:

- Verification emails
- Password reset emails
- Notifications
- Status (`sent`, `failed`, or log-only `logged`) with error messages

## Email Features

### Email Verification (SaaS)

- Verification email sent on SaaS signup; self-host registration uses
  administrator approval instead
- Link expires in 24 hours
- Admin can resend manually

### Password Reset

- With a real SMTP or Brevo provider, the user requests via `/forgot-password`
  and receives a reset link
- Link expires in 1 hour
- Without real delivery, the reset form and email-send actions report the
  capability as unavailable; an administrator can set a temporary password for
  an ordinary user and require replacement at the next login

### Email Provider

Configured via environment variables:

```bash
EMAIL_PROVIDER=smtp          # smtp, brevo, auto, none, or explicit test sink
EMAIL_FROM=noreply@domain    # Required for real delivery
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_REQUIRE_TLS=true
# SMTP_USER and SMTP_PASSWORD are optional, but must be supplied together
```

Real delivery supports generic SMTP and Brevo. With `EMAIL_PROVIDER=auto` (or
unset), a complete SMTP configuration takes precedence and a legacy
`BREVO_API_KEY` plus `EMAIL_FROM` configuration remains supported. The explicit
`test` provider logs messages and is never advertised as real delivery.

## Documentation Map

Where things are documented. After changing code, find the area below and update
the matching file in the same change.

### Public docs — MDX shells plus MCP-owned portable help

Rendered pages live under `packages/docs/src/content/docs/docs/` (EN) and
`packages/docs/src/content/docs/ru/docs/` (RU). Runtime-visible topics keep their semantic Markdown
under `packages/mcp-server/src/help/content/`, with Russian counterparts under `…/content/ru/`.
Each localized MDX shell imports its matching source through the MCP package. The `help` tool
discovers and reads the English sources directly; pages with an interactive insertion compose the
same authored before/after sections in runtime and public presentation. The special `tools` topic is
listed alongside those file-backed topics and renders directly from the typed MCP contract.

| Area            | Covers                                                                                                        | Path               |
| --------------- | ------------------------------------------------------------------------------------------------------------- | ------------------ |
| Getting started | Introduction (agent-first), quickstart, tutorial on the learning flows, which ready flow to use, self-hosting | `getting-started/` |
| Concepts        | Workflows, nodes, templates, notes, playbooks, artifacts                                                      | `concepts/`        |
| Guides          | Writing directives, creating & editing workflows, reading a flow and a run, the overview of runs, extensions  | `guides/`          |
| Reference       | Tools, input schema, magic variables, condition operators, validation, workflow catalog                       | `reference/`       |
| Integration     | MCP clients, Claude Code, agent guide, Telegram setup, troubleshooting                                        | `integration/`     |
| Patterns        | Branching, validation loop, escalation, subagent review, workspace, and more                                  | `patterns/`        |

### Internal docs — `docs/`

For contributors working on the codebase (implementation detail, not end-user docs).

| File                   | Covers                                                                                                                  | Path                                                              |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| Development setup      | Build, Docker, local dev, project structure                                                                             | `docs/DEVELOPMENT.md`                                             |
| Testing                | Test types, runner, fixtures, antipatterns                                                                              | `docs/TESTING.md` + `docs/testing/`                               |
| API                    | Backend & admin HTTP API reference                                                                                      | `docs/API.md`                                                     |
| System architecture    | Engine, storage, MCP transport, handlers, validation                                                                    | `docs/SYSTEM.md`                                                  |
| Authentication         | Better Auth, OAuth 2.1, API tokens                                                                                      | `docs/AUTHENTICATION.md`                                          |
| Web UI                 | Frontend architecture, components                                                                                       | `docs/WEB-UI.md`                                                  |
| Audit system           | Audit logging design                                                                                                    | `docs/AUDIT-SYSTEM.md`                                            |
| Workflows              | Workflow authoring, tools, catalog                                                                                      | `docs/WORKFLOW.md`, `docs/WORKFLOWS.md`, `docs/WORKFLOW-TOOLS.md` |
| Design system          | UI design tokens and components                                                                                         | `docs/DESIGN-SYSTEM.md`                                           |
| Documentation style    | How to write internal **and** public docs                                                                               | `docs/DOCUMENTATION-STYLE-GUIDE.md`                               |
| Logging                | Structured logging conventions                                                                                          | `docs/LOGGING.md`                                                 |
| Codespaces             | GitHub and local providers, companion setup/recovery, persistent lifecycle, MCP tools, website management and isolation | `docs/CODESPACES.md`                                              |
| Issue management       | GitHub issue conventions                                                                                                | `docs/ISSUE-MANAGEMENT.md`                                        |
| Architecture decisions | ADRs (licensing, OSS model, …)                                                                                          | `docs/adr/`                                                       |
| Deployment             | Environment variables, restart procedures                                                                               | `docs/deployment/`                                                |
| Legal                  | License/legal notes                                                                                                     | `docs/legal/`                                                     |

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the PR flow, DCO sign-off, and how
releases are automated (Conventional Commits → semantic-release → versioned GHCR
image). For upgrading a self-host instance, see [Updating / Upgrading](#updating--upgrading).

## License

[Apache License 2.0](LICENSE)

Maintenance

ActivityActive
ResponsivenessSlow