MCP server for DAG-based workflow execution with YAML definitions and LLM collaboration
Project description
Workflows MCP
Run YAML workflows as MCP tools so agents can automate real tasks with one server.
Why this project
workflows-mcp gives MCP clients a reusable automation layer:
- Define tasks once in YAML and run them from any MCP-compatible client.
- Orchestrate multi-step work with dependency-aware execution.
- Support interactive runs (
Promptblocks) with pause/resume. - Keep secrets server-side via
WORKFLOW_SECRET_*with redacted outputs. - Run synchronous or async jobs with queue visibility and cancellation.
Quickstart (5 minutes)
1) Install
Requires Python 3.12+.
uv pip install workflows-mcp
or:
pip install workflows-mcp
2) Add to your MCP client
Example (claude_desktop_config.json):
{
"mcpServers": {
"workflows": {
"command": "uvx",
"args": ["workflows-mcp"],
"env": {
"WORKFLOWS_TEMPLATE_PATHS": "/path/to/your/workflows",
"WORKFLOWS_LOG_LEVEL": "INFO",
"WORKFLOW_SECRET_API_KEY": "your-secret-value"
}
}
}
}
If installed with pip:
{
"mcpServers": {
"workflows": {
"command": "workflows-mcp",
"env": {
"WORKFLOWS_TEMPLATE_PATHS": "/path/to/your/workflows",
"WORKFLOWS_LOG_LEVEL": "INFO",
"WORKFLOW_SECRET_API_KEY": "your-secret-value"
}
}
}
}
3) Restart your MCP client and run first calls
list_workflowsget_workflow_infoexecute_workflow
Instructions for LLM Agents
Use this call order for reliable results:
- Discover: call
list_workflows. - Inspect: call
get_workflow_infofor required inputs. - Execute: call
execute_workflow. - Track async runs (if
mode="async"): callget_job_status(orlist_jobs). - Resume interactive workflows: call
resume_workflowonly forpausedjobs (typically fromPromptblocks). - Reload definitions after YAML edits: call
reload_workflows.
When authoring workflows, validate first:
validate_workflow_yamlbeforeexecute_inline_workflow.- Use
get_workflow_schemafor current schema details. - Use the block reference for exact field names and required inputs:
docs/llm/block-reference.md.
Async mini-flow example:
execute_workflow(workflow="python-ci-pipeline", inputs={...}, mode="async")
→ returns job_id
→ get_job_status(job_id="job_...") until completed/failed/paused
→ if paused, resume_workflow(job_id="job_...", response="...")
Available MCP tools (catalog + call patterns)
Workflow discovery and execution
| Tool | When to call | Typical call pattern |
|---|---|---|
list_workflows |
List registered workflows | list_workflows(tags=[], format="json") |
get_workflow_info |
Exploratory to confirm inputs/outputs | get_workflow_info(workflow="name", format="json") |
execute_workflow |
Run a registered workflow | execute_workflow(workflow="name", inputs={...}, mode="sync") |
execute_inline_workflow |
Test one-off YAML without registering | execute_inline_workflow(workflow_yaml="...", inputs={...}) |
reload_workflows |
After editing workflow YAML files on disk | reload_workflows() |
Authoring and validation
| Tool | When to call | Typical call pattern |
|---|---|---|
get_workflow_schema |
Debugging only. Retrieve full JSON schema for authoring | get_workflow_schema() |
validate_workflow_yaml |
Validate YAML before execution | validate_workflow_yaml(yaml_content="...") |
Async, queue, and interactive control
| Tool | When to call | Typical call pattern |
|---|---|---|
get_job_status |
Poll a specific async job | get_job_status(job_id="job_...") |
list_jobs |
Find jobs by status (especially paused) | list_jobs(status="paused", limit=100) |
cancel_job |
Stop queued/running jobs | cancel_job(job_id="job_...") |
get_queue_stats |
Monitor queue health/capacity | get_queue_stats() |
resume_workflow |
Continue paused Prompt workflows |
resume_workflow(job_id="job_...", response="...") |
Memory (conditional)
| Tool | When to call | Typical call pattern |
|---|---|---|
memory |
Unified memory query/ingest/maintenance/graph operations | memory(operation="query", scope={...}, query={...}) |
project_onboard |
Current memory onboarding flow with checkpoints | project_onboard(scope={...}, ingest={...}, max_operations=1) |
project_sync |
Current memory checkpoint resume/continuation | project_sync(checkpoint={...}, max_operations=3) |
IMPORTANT: The memory tool is registered only when memory DB setup is available and valid at startup (see below)
Memory contract highlights:
- Unified envelope:
operation+ optionalscope/query/record/graph/maintenance/response. - Current memory taxonomy:
scopeaccepts onlypalace,wing,room,compartment. - Context activation and scope defaulting:
- Resolution precedence is
scope→scope_token→context_id(per-field merge). scope_tokenresolves from execution contextmemory_scope_tokens;context_idresolves frommemory_context_scopes.- Responses include
resolved_scopeandscope_sourcewhen available. - Required scope by operation:
query: all four fields must resolve.ingest: all four fields must resolve (includingcompartment).graph_upsertwithgraph.kind="place": all four fields must resolve.validate|supersede|archive|maintain|graph_delete|graph_upsert(kind="link"): scope is optional.
- Resolution precedence is
- Temporal query semantics:
operation="query"supports eitherquery.as_ofOR intervalquery.from/query.to(mutually exclusive).query.mode="graph"supportsquery.as_ofonly;query.from/query.toare rejected.operation="ingest"withrecord.format="raw"supportsrecord.valid_fromandrecord.valid_towith ordering validation (valid_from <= valid_to).
- Strict validation: unknown/extra fields are rejected.
- Direct vs derived boundaries:
operation="ingest"is direct-only (record.memory_tiermust bedirect).- Category governance for ingest is explicit:
- Unknown
record.categoriesfail deterministically withMEM_UNKNOWN_CATEGORYwhenrecord.allow_create_categories=false(default behavior). - Setting
record.allow_create_categories=trueopts into category creation and allows ingest to proceed when categories are otherwise valid.
- Unknown
- Derived/community artifacts are produced by maintenance flows (for example
maintenance.mode="community_refresh"). query.mode="communities"uses the dedicated communities strategy.
- Lifecycle semantics:
- Archived records are excluded by default query behavior.
operation="supersede"requiresrecord.superseded_by.operation="archive"maps to forget semantics; repeating archive on already archived records is safe/idempotent.
- Graph semantics:
operation="graph_upsert"withgraph.kind="link"is idempotent.operation="graph_delete"returns compact delete counters (deleted_placesforkind="place",deleted_linksforkind="link"); optional debug output adds diagnostics metadata.
Validation note: this ingest category behavior (MEM_UNKNOWN_CATEGORY with create disabled, successful ingest with allow_create_categories=true) was live-validated on 2026-04-21 via production-like direct MCP memory calls.
Direct-call JSON examples:
query:
{
"operation": "query",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"query": {"mode": "search", "text": "schema epoch", "as_of": "2026-04-20T00:00:00Z", "radius": 1}
}
ingest:
{
"operation": "ingest",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"record": {"format": "raw", "content": "Memory active", "memory_tier": "direct"}
}
validate:
{
"operation": "validate",
"record": {"ids": ["11111111-1111-1111-1111-111111111111"]}
}
supersede:
{
"operation": "supersede",
"record": {
"ids": ["11111111-1111-1111-1111-111111111111"],
"superseded_by": "22222222-2222-2222-2222-222222222222"
}
}
archive:
{
"operation": "archive",
"record": {"ids": ["11111111-1111-1111-1111-111111111111"]}
}
maintain:
{
"operation": "maintain",
"maintenance": {"mode": "community_refresh"}
}
graph_upsert (kind=place):
{
"operation": "graph_upsert",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"graph": {"kind": "place", "place_name": "Memory API (current)", "place_type": "feature"}
}
graph_upsert (kind=link):
{
"operation": "graph_upsert",
"graph": {
"kind": "link",
"from": "11111111-1111-1111-1111-111111111111",
"to": "22222222-2222-2222-2222-222222222222",
"link_type": "depends_on"
}
}
graph_delete:
{
"operation": "graph_delete",
"graph": {"kind": "place", "ids": ["33333333-3333-3333-3333-333333333333"]}
}
project_onboard:
{
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"ingest": {"format": "raw", "content": "Initial baseline", "memory_tier": "direct"},
"supersede": {"ids": ["11111111-1111-1111-1111-111111111111"], "superseded_by": "22222222-2222-2222-2222-222222222222"},
"archive": {"ids": ["33333333-3333-3333-3333-333333333333"]},
"maintain": {"mode": "community_refresh"},
"max_operations": 1
}
project_sync:
{
"checkpoint": {
"version": "oss-r2",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"plan": [{"operation": "ingest", "payload": {"format": "raw", "content": "Initial baseline", "memory_tier": "direct"}}],
"next_index": 0,
"completed": []
},
"max_operations": 3
}
Invalid (mutually exclusive temporal filters):
{
"operation": "query",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"query": {
"mode": "search",
"text": "maintenance",
"as_of": "2026-04-20T00:00:00Z",
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-20T00:00:00Z"
}
}
Invalid (graph query rejects interval filters):
{
"operation": "query",
"scope": {"palace": "acme", "wing": "workflows", "room": "memory-engine", "compartment": "contract-r2"},
"query": {
"mode": "graph",
"text": "service graph",
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-20T00:00:00Z"
}
}
Invalid (legacy taxonomy key):
{
"operation": "query",
"scope": {"palace": "acme", "wing": "svc", "room": "component", "hall": "legacy"},
"query": {"text": "find this", "mode": "search"}
}
Invalid (supersede missing required superseded_by):
{
"operation": "supersede",
"record": {
"ids": ["11111111-1111-1111-1111-111111111111"]
}
}
Configuration
Workflow loading and execution
WORKFLOWS_TEMPLATE_PATHS: Comma-separated workflow directories to load.WORKFLOWS_MAX_RECURSION_DEPTH: Max workflow composition depth (default:50).WORKFLOWS_LOG_LEVEL: Logging level (DEBUG,INFO,WARNING,ERROR,CRITICAL; default:INFO).
Queue and async settings
WORKFLOWS_IO_QUEUE_ENABLED: Enable serialized I/O queue (default:true).WORKFLOWS_JOB_QUEUE_ENABLED: Enable async job queue (default:true).WORKFLOWS_JOB_QUEUE_WORKERS: Queue workers (default:3).WORKFLOWS_MAX_CONCURRENT_JOBS: Max active + queued jobs (default:500).WORKFLOWS_JOB_TIMEOUT: Default async timeout in seconds (default:3600).WORKFLOWS_JOB_HISTORY_MAX: Max retained job records (default:1000).WORKFLOWS_JOB_HISTORY_TTL: Job retention TTL in seconds (default:86400).
Secrets and LLM config
WORKFLOW_SECRET_<NAME>: Secret value exposed as{{secrets.NAME}}.WORKFLOWS_LLM_CONFIG: Optional path override for LLM config.
Memory
Memory is an optional persistent storage feature that lets agents and workflows record, retrieve, and organize information across sessions. It is backed by PostgreSQL and gives each agent a structured, scoped, and temporally-aware knowledge store.
What memory provides
memoryMCP tool — direct call interface for LLM agents to store and query information without writing workflow YAML.project_onboard/project_syncMCP tools — Current memory contract helpers for checkpointed onboarding/sync sequences.Memoryworkflow block — use inside YAML workflows to automate memory operations as part of larger pipelines.- Memory topology scoping (
palace→wing→room→compartment) — current memory scope keys are strict and legacy keys (for examplehall) are rejected. Scope can be supplied directly and/or resolved from context (scope_token,context_id) using deterministic precedence. - Temporal tracking — records carry
valid_from/valid_totimestamps supporting point-in-time and interval queries. - Knowledge graph — link memories to places, entities, or concepts and query the resulting graph.
- Lifecycle management — archive or supersede records without deletion; archived records are excluded from default queries.
Retrieval strategies
Current memory behavior exposes query modes that map to retrieval strategies internally:
| Query mode / trigger | Effective strategy | Behavior |
|---|---|---|
query.mode="search" + radius=0 |
palace |
Strict scoped retrieval lane (no companion lane). |
query.mode="search" + radius>=1 |
auto |
Scoped lane + optional S2 companion lane (s2_enabled=true by default). |
query.mode="hybrid" |
auto |
Same retrieval family as auto with fused ranking. |
query.mode="context" |
context |
Context assembly retrieval path. |
query.mode="graph" |
graph |
Graph traversal/path/stats retrieval. |
query.mode="communities" |
communities |
Community-focused retrieval strategy. |
Every query call still requires a fully resolved current memory scope (palace/wing/room/compartment) resolved from request and/or context sources.
Scope call shape (with context activation)
Scope fields can be passed directly and/or resolved from scope_token / context_id:
{
"operation": "query",
"scope": {"palace": "acme", "wing": "my-service"},
"scope_token": "st_auth",
"context_id": "ctx_default",
"query": {"text": "refresh token lifetime", "mode": "context"}
}
Resolution precedence is scope → scope_token → context_id for each field.
Prerequisites
- PostgreSQL 13 or later, network-accessible from the server process.
- A database user with
CREATE DATABASErights on the admin database (typicallypostgres) if auto-create is enabled, or a pre-existing database that the user can connect to.
Configuration
| Variable | Default | Required | Description |
|---|---|---|---|
MEMORY_DB_HOST |
— | Yes | PostgreSQL hostname or IP. Setting this variable enables memory features. |
MEMORY_DB_PORT |
5432 |
No | PostgreSQL port. |
MEMORY_DB_NAME |
memory_db |
No | Target database name. |
MEMORY_DB_USER |
— | Yes | Database username. |
MEMORY_DB_PASSWORD |
— | Yes | Database password. |
MEMORY_DB_AUTO_CREATE |
true |
No | Auto-create the target database on first boot if it does not exist. Requires the user to have CREATE DATABASE rights on the admin database. |
MEMORY_DB_ADMIN_DATABASE |
postgres |
No | Admin database used to issue the CREATE DATABASE statement when auto-create is enabled. |
AUDIT_FAIL_CLOSED |
false |
No | When true, audit-logging failures abort the entire memory operation (compliance mode). Default is log-and-continue. |
Enabling memory
Add the following variables to your MCP client config:
{
"mcpServers": {
"workflows": {
"command": "uvx",
"args": ["workflows-mcp"],
"env": {
"MEMORY_DB_HOST": "localhost",
"MEMORY_DB_PORT": "5432",
"MEMORY_DB_NAME": "memory_db",
"MEMORY_DB_USER": "postgres",
"MEMORY_DB_PASSWORD": "your-password"
}
}
}
}
On first boot with MEMORY_DB_AUTO_CREATE=true (the default), the server creates the target database and applies the schema automatically. Restart your MCP client after adding the variables.
Verifying memory is active
- Restart your MCP client.
- Call
list_workflows— confirm the server started without errors in the client logs. - Check that the
memorytool appears in the available tool list. - Run a test ingest:
{
"operation": "ingest",
"scope": {"palace": "acme", "wing": "test", "room": "setup", "compartment": "smoke"},
"record": {"format": "raw", "content": "Memory is working.", "memory_tier": "direct"}
}
If the tool is absent, check server logs for MEMORY_DB_* startup errors — the most common causes are an unreachable host, incorrect credentials, or a schema epoch mismatch.
Schema compatibility
Memory schema versions are tracked by epoch. If the epoch in the database does not match the server version:
- Startup fails with an explicit error.
- Apply the documented migration and restart.
- No automatic destructive reset is performed. Set
MEMORY_SCHEMA_RESET_MODEonly if you intend a one-time destructive reset on a non-production instance.
Workflows for users
Use registered workflows for repeatable automation, and inline workflows for experiments.
- Registered workflows: best for shared, reusable operations.
- Inline workflows: best for quick tests and prototyping.
Author workflow YAML with exact block input names from:
docs/llm/block-reference.md
Example block families include Shell, ReadFiles, HttpCall, LLMCall, Sql, Workflow, Prompt, and Memory.
Documentation map
README.md: install, usage, and tool catalog.docs/guides/memory-tools-cheatsheet.md: Current memory contract quick reference, detailed guide, and examples.docs/llm/block-reference.md: exact block inputs/outputs for workflow authoring.docs/TESTING.md: test strategy and test commands.ARCHITECTURE.md: architecture overview.docs/adr/: design decisions and rationale.CHANGELOG.md: release history.
Contributing
- Fork the repository and create a focused branch.
- Add or update tests with your change.
- Run quality checks before opening a PR:
uv run pytest
uv run ruff check src/workflows_mcp/
uv run mypy src/workflows_mcp/
- Describe behavior changes and config impact clearly in the PR.
Support and community
- Issues and bug reports: https://github.com/qtsone/workflows-mcp/issues
- Project repository: https://github.com/qtsone/workflows-mcp
License
AGPL-3.0-or-later. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file workflows_mcp-10.0.0.tar.gz.
File metadata
- Download URL: workflows_mcp-10.0.0.tar.gz
- Upload date:
- Size: 608.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5c58db50ea2200412b58c0665a0bdfa3ea7c4810fa61bc6a521c0db3377b018
|
|
| MD5 |
af2b0ab0111c670f8bf54a326ab29810
|
|
| BLAKE2b-256 |
d37cd9df024545714144330684d23f524449b158879e982572ed8230dd76b6a5
|
Provenance
The following attestation bundles were made for workflows_mcp-10.0.0.tar.gz:
Publisher:
publish-pypi.yml on qtsone/workflows-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
workflows_mcp-10.0.0.tar.gz -
Subject digest:
c5c58db50ea2200412b58c0665a0bdfa3ea7c4810fa61bc6a521c0db3377b018 - Sigstore transparency entry: 1353167944
- Sigstore integration time:
-
Permalink:
qtsone/workflows-mcp@26cd3a334dd289c656162f2e011d49b1b3f03622 -
Branch / Tag:
refs/tags/v10.0.0 - Owner: https://github.com/qtsone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@26cd3a334dd289c656162f2e011d49b1b3f03622 -
Trigger Event:
push
-
Statement type:
File details
Details for the file workflows_mcp-10.0.0-py3-none-any.whl.
File metadata
- Download URL: workflows_mcp-10.0.0-py3-none-any.whl
- Upload date:
- Size: 315.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fa18509ebfecbec318f541b08ab87623f705add874ade6dc9f229dc84a0ddb0
|
|
| MD5 |
f27a58ce00c7ab275c2aeab63e7e4776
|
|
| BLAKE2b-256 |
0f564f5fe68a81ce15edbc91ba5cca79ec2910ffc0b0384046031e34fb23ec13
|
Provenance
The following attestation bundles were made for workflows_mcp-10.0.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on qtsone/workflows-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
workflows_mcp-10.0.0-py3-none-any.whl -
Subject digest:
6fa18509ebfecbec318f541b08ab87623f705add874ade6dc9f229dc84a0ddb0 - Sigstore transparency entry: 1353168050
- Sigstore integration time:
-
Permalink:
qtsone/workflows-mcp@26cd3a334dd289c656162f2e011d49b1b3f03622 -
Branch / Tag:
refs/tags/v10.0.0 - Owner: https://github.com/qtsone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@26cd3a334dd289c656162f2e011d49b1b3f03622 -
Trigger Event:
push
-
Statement type: