dify-mcp
The most complete MCP server + CLI for Dify
192 tools. 21 namespaces. One registry. Let any AI agent build, test, and ship Dify workflows autonomously — everything a human can do in the UI, now scriptable.
Works with 24 popular AI agents — Claude Code, Codex, Gemini CLI, Cursor, Cline, Windsurf, Roo Code, Continue, Zed, Aider, OpenCode, Antigravity, GitHub Copilot, Goose, Trae, Kilo Code, Warp, Crush, Droid, Amp, OpenHands, Cody, Augment, and Amazon Q Developer — plus any other MCP-compatible or shell-capable agent.
What is this?
Dify is a powerful open-source LLM app platform — but its workflow builder is a visual drag-and-drop editor. What if you want an AI agent to programmatically create workflows, wire up nodes, test them, iterate, and publish — without a browser?
dify-mcp is the bridge. It exposes the entire Dify console API as a unified tool registry with two surfaces: a CLI any shell-capable agent can drive, and an MCP server (stdio or Streamable HTTP) any MCP-compatible host can attach. Same 192 tools, same JSON contract, same safety guarantees.
┌──────────────────────────────────────────────────────────┐
│ dify-mcp │
│ │
│ ┌──────────┐ ┌────────────────────┐ ┌─────────┐ │
│ │ CLI │───▶│ 192-tool │───▶│ Dify │ │
│ │ difywf │ │ registry │ │ API │ │
│ └──────────┘ │ │ └─────────┘ │
│ ┌──────────┐ │ app · workflow │ ▲ │
│ │ MCP │───▶│ knowledge · rag │─────────┘ │
│ │ stdio │ │ agent · snippet │ │
│ └──────────┘ │ workspace · ... │ │
│ └────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Works with your favorite agents
dify-mcp is agent-agnostic by design — no SDK lock-in, no proprietary protocol. If your agent can run a shell command, it can use the CLI. If it speaks MCP, it can attach the server. Most popular agents do both:
Don't see your agent? If it supports MCP or can run shell commands, it works. The connect section below has copy-paste configs for each host.
Why you'll star this
-
Complete coverage. Not a subset. Not a wrapper around the public API alone. This talks to the internal console API — the same surface the Dify web UI uses. Create apps, author graphs node-by-node, validate, test-run, publish, manage versions, triggers, providers, plugins, RAG pipelines, snippets, agent configs, comments, annotations, audio, stats. If the UI can do it, so can your agent.
-
Agent-agnostic by design. No SDK lock-in. The CLI works with any agent that can run a shell command. The MCP server works with any MCP host. Both return structured JSON —
{ ok, data }or{ ok: false, error: { code, message, retryable } }— so agents never scrape human-readable text. For large drafts and exports, the CLI's--output-file <path>keeps the full UTF-8 result off size-limited stdout transports. -
Cookie auth, handled. Dify's console uses cookie + CSRF double-submit, not Bearer tokens. dify-mcp captures, stores (keychain on macOS, else
0600file), and auto-refreshes the session — including server-side refresh-token rotation. That same session now covers draft and published run/stop, file upload, dependency checks, and workspace switch. MCP hosts can callauth.import_cookies/auth.login_consolewithout the CLI.DIFY_CONSOLE_COOKIEand--console-cookiework for non-interactive bootstrap. -
Safe by default. Destructive operations require explicit
confirm=true/--yes. Graphs are validated offline before sync (iteration/loop sub-graphs, sticky notes, and modern multi-case if-else included). Every mutation is audit-logged.--dry-runshows diffs without saving. -
Zero build step. Runs directly on Node 23.6+ native TypeScript. No compiler, no bundler, no transpiler. Clone, install, go.
What's new in v0.4.0
- Workspace MCP tool providers (
mcp.*). Connect external MCP servers to your Dify workspace:mcp.list,mcp.get,mcp.create,mcp.update,mcp.delete(confirm-gated), andmcp.refresh_tools. - App MCP Server access points (
app.mcp_server_*). Expose any Dify workflow/agent app as an MCP server:app.mcp_server_get,app.mcp_server_set, andapp.mcp_server_rotate(confirm-gated token regeneration). - Workspace Skills (
skill.*). Full skill lifecycle and agent skill bindings:skill.list,skill.get,skill.versions,skill.references,skill.import,skill.publish,skill.delete(confirm-gated),skill.agent_bindings, andskill.set_agent_bindings. - Dify 1.17.1 compatibility & fixes. Verified compatibility against Dify
1.17.1upstream console routes; fixed input validation ordering insnippet.import.
What's new in v0.3.0
- Classic knowledge bases (
knowledge.*). Dataset CRUD, document create fromfile.uploadids (or full KnowledgeConfig), rename/delete, indexing status, hit-testing, and segment add/update/delete — confirm-gated where destructive. - Workspace member admin.
workspace.invite_members,workspace.update_member_role, andworkspace.remove_member(confirm-gated). - MCP progress notifications. Long SSE runs (
workflow.run_draft, chat, …) emitnotifications/progresswhen the host supplies aprogressTokenintools/call_meta. - Local backup and migration.
app.backupbulk-exports DSLs plus a manifest;app.restoredry-runs conflicts and imports to another configured Dify instance. - Explicit compatibility matrix. Dify
1.17.xand1.16.xare supported; older versions are intentionally unsupported.
What's new in v0.2.0
- Cookie-complete authoring. Run, stop, upload, check deps, and switch workspaces with the same console session. MCP hosts authenticate without dropping to the CLI.
- Safer HTTP MCP. Binds
127.0.0.1by default. Binding0.0.0.0(Docker) requiresDIFYWF_MCP_TOKEN. Host allowlist + 2MB body cap;/healthstays open for probes. - Workflow-as-tool providers. Get, refresh, or delete the published-tool
binding after you ship a version (
workflow.tool_get/workflow.tool_refresh_provider/workflow.tool_delete). - Verified app tags.
app.ensure_tag/app.remove_tagbind or unbind an exact tag name and read it back. Confirm-gated. - Smarter graph validation. Iteration/loop inner nodes, canvas
custom-notestickies, and moderncases[]if-else branches no longer fail offline checks. - Large payloads.
--yaml @filefor DSLs that exceed OS argument limits;--output-filekeeps big results off size-limited stdout. - Starter graphs. Ready-made templates in
examples/(echo, LLM, RAG).sync_draftno longer wipes omitted env/conversation secrets.
Live verified
Authoring loop verified against cloud.dify.ai 1.17.0 with console cookie auth:
- ✅ Create app → sync draft (echo graph) → run draft → delete (MCP Streamable HTTP)
- ✅ MCP transport:
tools/callover stdio and Streamable HTTP - ✅ Example templates in
examples/validate clean (echo, LLM, RAG) - ✅ Unit tests · typecheck clean · MCP smoke (192 tools)
Dify compatibility
Cloud and self-hosted Dify releases can expose different console contracts even
within a minor series. When reporting an issue, include the response header
x-version (or your deployment version), the difywf --version output, and the
exact command/tool call. DSL version drift is a hard failure
(DSL_VERSION_MISMATCH), not something the agent should edit around.
Not every one of the 192 tools is live-probed on every release. Coverage is densest on the authoring path (apps, workflow draft/run/publish, auth, MCP guardrails). Surfaces such as knowledge bases, RAG pipelines, snippets, agents, annotations, and audio are implemented against the console API contract and unit-tested; treat them as best-effort until you exercise them on your instance.
Known gaps vs the full Dify UI
Still deferred (not blocking for workflow authoring):
- External knowledge / connector-specific ingest UIs beyond the classic dataset APIs
- Fine-grained dataset permission member pickers in the UI sense (API body passthrough only)
Quickstart
Prerequisites: Node >= 23.6 (native TypeScript type stripping — no build step).
git clone https://github.com/alexjiaguo/dify-mcp.git
cd dify-mcp
npm install
npm link # puts `difywf` on your PATH (optional)
Authenticate
The Dify console uses cookie + CSRF auth. The easiest path:
# 1. Export cookies from your browser (cookie-editor extension → Export → JSON)
# 2. Save as cookies.json, then:
difywf auth import-cookies --base-url https://cloud.dify.ai --file cookies.json
# Or self-hosted with email/password (no browser needed):
difywf auth login-console --base-url https://your-dify --email you@x --password '***'
# Non-interactive bootstrap can keep credentials out of process arguments:
DIFY_CONSOLE_EMAIL=you@x DIFY_CONSOLE_PASSWORD='***' \
difywf auth login-console --base-url https://your-dify
# Deployments whose login endpoint expects the legacy encoded payload:
DIFY_CONSOLE_EMAIL=you@x DIFY_CONSOLE_PASSWORD='***' \
DIFY_CONSOLE_PASSWORD_ENCODING=base64 \
difywf auth login-console --base-url https://your-dify
difywf auth status # confirm: shows base URL + cookie names (values masked)
# Non-interactive cookie bootstrap (Cookie header or cookie-editor JSON):
# DIFY_CONSOLE_COOKIE='console_token=...; csrf_token=...; refresh_token=...' \
# difywf auth status --base-url https://cloud.dify.ai
Build a workflow
difywf agent guide # self-onboarding playbook for agents
difywf app list # see your apps
difywf app create --mode workflow --name "my-agent-workflow"
difywf wf node defaults <app-id> llm # get the schema for an LLM node
difywf wf validate --graph examples/llm-workflow.json # offline: structure, refs, cycles
difywf wf draft sync <app-id> --graph examples/llm-workflow.json --dry-run
difywf wf draft sync <app-id> --graph examples/llm-workflow.json
difywf wf test <app-id> --input query="hello" # test-run the draft
difywf app import --yaml @workflow.yml --yes # file channel for large DSLs
difywf app ensure-tag <app-id> production --yes # bind + verify exact tag name
difywf wf publish <app-id> --yes # ship it
difywf workflow tool refresh-provider <app-id> --yes # rebind workflow-as-tool to the published version
Starter graphs live in examples/: minimal-workflow.json (echo),
llm-workflow.json (start → LLM → answer), rag-workflow.json (knowledge
retrieval). All three pass difywf wf validate with no error-level issues.
Back up and migrate
# Source instance: writes one DSL per app plus manifest.json (secrets excluded)
difywf app backup ./dify-backup
# Target instance: authenticate or set DIFY_API_BASE first, then preview
difywf app restore ./dify-backup --dry-run
# Import after reviewing name conflicts; skip is the default
difywf app restore ./dify-backup --yes
Filters are available for larger workspaces: --app-ids <id...>, --mode workflow,
--name production, and --limit 50. Backups are local files with mode 0600.
include_secret=true and overwrite=true both require --yes. Scheduled jobs,
Git, and S3-compatible storage are intentionally not bundled; use dify-dsl-pipe
when you need a dedicated backup service.
Connect your agent (MCP)
Same binary, same 192 tools. Copy-paste the config for your host:
There is no single universal MCP filename. What every host needs is the same
local stdio launch command: difywf mcp serve. The examples below show the
wrapper your host expects.
claude mcp add dify -- difywf mcp serve
[mcp_servers.dify]
command = "difywf"
args = ["mcp", "serve"]
{
"mcpServers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
{
"mcpServers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
Add an MCP server in Windsurf settings (Cmd+, -> MCP Servers) with command difywf
and args ["mcp", "serve"].
{
"context_servers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
Aider doesn't support MCP, but it can run shell commands. Just use the CLI directly:
/run difywf app list
/run difywf wf draft sync <app-id> --graph graph.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"dify": {
"type": "local",
"command": ["difywf", "mcp", "serve"],
"enabled": true
}
}
}
{
"servers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
extensions:
dify:
type: stdio
name: dify
enabled: true
cmd: difywf
args: ["mcp", "serve"]
mcp add dify --command difywf --args mcp serve
{
"mcpServers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
Most modern agents expose a local stdio MCP setting that uses the mcpServers JSON shape below. If your agent follows the Codex-style TOML format, use the second example:
{
"mcpServers": {
"dify": {
"command": "difywf",
"args": ["mcp", "serve"]
}
}
}
[mcp_servers.dify]
command = "difywf"
args = ["mcp", "serve"]
If your agent only runs shell commands, use the CLI directly:
difywf agent guide
difywf app list
For remote or containerized hosts that can't spawn a local process, run the MCP server over the stateless Streamable HTTP transport:
difywf mcp serve --http --host 127.0.0.1 --port 8080
# or via env: DIFYWF_MCP_TRANSPORT=http DIFYWF_MCP_HOST=127.0.0.1 \
# DIFYWF_MCP_PORT=8080 difywf mcp serve
Loopback binds do not need a token. Binding 0.0.0.0 (including Docker)
requires DIFYWF_MCP_TOKEN. Clients send Authorization: Bearer <token> or
x-difywf-token. GET /health stays unauthenticated for probes.
Point any Streamable-HTTP-capable client at http://<host>:8080/mcp. Each POST is
a self-contained JSON-RPC message (initialize / tools/list / tools/call); no
session is required. GET and DELETE requests to /mcp are rejected with 405.
To run it as a Docker service:
docker build -t dify-mcp .
docker run -d --name dify-mcp \
-p 3000:3000 \
-e DIFY_API_BASE=https://your-dify.example.com \
-e DIFYWF_MCP_TOKEN=generate-a-long-random-token \
-e DIFY_CONSOLE_COOKIE='console_token=...; csrf_token=...; refresh_token=...' \
-v difywf-home:/home/node/.difywf \
dify-mcp
The MCP URL to configure in Dify is http://<docker-host>:3000/mcp plus the
Bearer token. When Dify and this service run in the same Docker Compose network,
use the service name, for example http://dify-mcp:3000/mcp. The container
health endpoint is GET /health. The difywf CLI is also available inside the
container, for example docker exec dify-mcp difywf --version.
Prefer Docker secrets or your deployment platform's secret store instead of putting cookies or tokens in the image.
No
difywfon PATH? Use the absolute path:node /path/to/dify-mcp/bin/difywf.js mcp serve.
Tools
192 tools across 21 namespaces. Run difywf --help for the full live list, or
difywf agent guide for the agent-oriented playbook.
Safety
Error/exit codes: USAGE_ERROR(2), AUTH_REQUIRED(3), CONFIRM_REQUIRED(4),
VALIDATION_FAILED(5), RBAC_DENIED(6), NOT_FOUND(7), DSL_VERSION_MISMATCH(8),
RATE_LIMITED(9), SERVER_ERROR(10), NETWORK_ERROR(11). Check error.retryable
before retrying.
Environment
See .env.example. Common knobs:
Develop
npm test # 122 unit tests
npm run typecheck # tsc --noEmit
npm run smoke:mcp # MCP stdio smoke (192 tools, JSON-RPC handshake)
npm run smoke:mcp:http # MCP Streamable HTTP smoke (stateless POST /mcp)
No build step. Source runs directly via Node's type stripping. GitHub Actions
runs typecheck, unit tests, and both MCP smokes on every push to main.
Contributions welcome — see CONTRIBUTING.md.
License
Apache-2.0 — see LICENSE.
If this saves you time, a ⭐ is the best thank-you.
Built for agents, by agents.