Moira
io.github.moira-mcp/moira · 115 stars · TypeScript · Apache-2.0
MCP server Agent Workflow Engine — multi-step MCP workflows with per-step directives and validation.
Install
The repo has no one-line install. Follow its README.
Published as
- Docker
ghcr.io/moira-mcp/moira:0.3.4Streamable HTTP
From the server's entry in the official MCP Registry.
Files
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 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 in a single Docker container on your own infrastructure. Free and open source; your data stays with you. Start in the Quick Start below.
- Moira Cloud (managed) — a hosted instance with nothing to operate, at 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/ - User-owned Docker Sandboxes companion for local codespaces
- 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 configurationlogging/- 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:
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
The image is pulled from the public registry by default. Data (SQLite + execution
storage) persists in ./data. See Self-Hosting or the in-app docs
at /docs/ for the full reference.
Updating / Upgrading
Update a normal self-host installation with the standard Compose commands:
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, with a matching Russian version. Release notes are on the GitHub 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:
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 communication-channel bundles run in the separate extension runner and are disabled by default. Enabling them requires this source checkout because the runner image is built locally; the published Moira image does not import or package bundle code.
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
and Writing an
Extension 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 your computer to your Moira account through outbound HTTPS. It runs approved repositories in persistent, mountless Docker Sandboxes microVMs. GitHub Codespaces remains a separate provider. Public read-only local work needs no GitHub App connection; enrolled private Git and write operations use the account's server-held GitHub authorization.
The companion requires Node.js 24, macOS on Apple silicon and the pinned Docker Sandboxes
sbx 0.46.0 runtime. Linux execution is refused. From this source checkout:
npm ci npm run local:build npm run local -- --help
The package is private and is not a published global npm installer. Configure local repository
rights and a finite work lease before connecting Settings → Development → Local computers.
The pairing token goes to the companion through stdin; review the computer's grants in the browser
before confirming. See Codespaces: Moira Local
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.
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):
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
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
- All API routes (/api/) require authentication (except /api/auth/)
- Centralized protection via middleware (no manual checks)
Testing:
docker compose up -d # Access: http://localhost:8080/login # MCP Inspector: http://localhost:8080/mcp
See 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 |
{
"mcpServers": {
"moira-local": { "url": "http://localhost:8080/mcp" }
}
}
Replace
localhost:8080with 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 for every supported type and its current contract, including automatic note operations and file materialization.
Start Node
{
"type": "start",
"id": "start",
"connections": { "default": "next-node-id" }
}
Agent Directive Node
{
"type": "agent-directive",
"id": "task",
"directive": "Task instruction",
"completionCondition": "Success criteria",
"inputSchema": {/* JSON Schema */},
"connections": { "success": "next-node-id" }
}
Condition Node
{
"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
{
"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
{
"type": "end",
"id": "end",
"finalOutput": ["result", "score"]
}
Expression Node
{
"type": "expression",
"id": "increment-counter",
"expressions": ["counter = counter + 1"],
"connections": { "default": "next-step" }
}
Teleport Node
{
"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
{
"type": "subgraph",
"id": "run-subtask",
"graphId": "subtask-workflow",
"inputMapping": { "parentVar": "subVar" },
"outputMapping": { "subResult": "parentResult" },
"connections": { "success": "next-step" }
}
Lock Node
{
"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
{
"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
# 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": "attachment-token", "message": "Report", "kind": "document", "filename": "report.pdf", "mimeType": "application/pdf", "sizeBytes": 12000}
# Documentation
help
help {"topic": "tools"}
help {"topic": "step"}
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.
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 - mandatory pre-commit checks executed by development workflows.
Claude Code Commands
Custom slash commands in /commands directory. See 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
POST /mcp # JSON-RPC requests (tools calls) GET /health # Server health check
Environment Variables
# 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 for details):
{
"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.
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:
anytypes are errors, must be properly typed - Test code:
anytypes allowed for flexibility
Security
Rate Limiting
Protection against spam and DoS attacks with tiered limits:
- API routes (
/api/*): 100 requests/minute - Auth routes (
/api/auth/*): 100 requests/minute - MCP endpoint (
/mcp): 30 requests/minute
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:
{
"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-onlylogged) 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-passwordand 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:
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 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.
License
Facts
- Kind
- MCP server
- Repo
- moira-mcp/moira
- Group
- Uncategorized
- Stars
- 115
- License
- Apache-2.0
- Language
- TypeScript
- Last push
- 2026-10-11
- MCP Registry
- io.github.moira-mcp/moira
- Topics
- agent-workflow, ai-agents, docker, mcp, model-context-protocol, self-hosted, typescript, workflow, workflow-engine
- 1REAio.github.morluto/reaReverse engineer anything from your terminal or agent with one CLI and MCP server.125.3k
- 2com.browser-use/browser-usecom.browser-use/browser-useControl a real Chrome browser to complete any task: fill forms, extract data, book flights.117.7k
- 3World Monitorapp.worldmonitor/mcpLive markets, conflicts, country risk, chokepoints, energy, and China decision signals. 93 tools.88.3k
- 4Scrapling MCP Serverd4vinci/scrapling🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scraplingdev86.9k
- 5Everythingmodelcontextprotocol/serversThis MCP server attempts to exercise all the features of the MCP protocol. It is not intended to be a useful server, but rather a test server for builders of MCP clients. It implements prompts, tools, resources, sampling, and more to showcase MCP capabilities.85.8k
- 6Fetchmodelcontextprotocol/serversA Model Context Protocol server that provides web content fetching capabilities. This server enables LLMs to retrieve and process content from web pages, converting HTML to markdown for easier consumption.85.8k