Skip to content

Commit e5291b9

Browse files
YangYang
Yang
authored and
Yang
committed
init code
1 parent f650673 commit e5291b9

File tree

7 files changed

+409
-0
lines changed

7 files changed

+409
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mcp-difyworkflow-server

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## mcp-difyworkflow-server
2+
3+
mcp-difyworkflow-server is an mcp server Tools application that implements the query and invocation of Dify workflows, supporting the on-demand operation of multiple custom Dify workflows.
4+
5+
## INSTALL
6+
7+
```shell
8+
git clone https://github.com/gotoolkis/mcp-difyworkflow-server.git
9+
10+
cd mcp-difyworkflow-server
11+
go build .
12+
```
13+
14+
## Configuration
15+
16+
```json
17+
{
18+
"mcpServers": {
19+
"mcp-difyworkflow-server": {
20+
"command": "mcp-difyworkflow-server",
21+
"args": ["-base-url", "http://localhost/v1"],
22+
"env": {
23+
"DIFY_WORKFLOW_NAME": "workflow-translator“,workflow-genImag",
24+
"DIFY_API_KEYS": "appkey-xxxxxxxxxxxa,appkey-xxxxxxxxxxxb"
25+
}
26+
}
27+
}
28+
}
29+
```
30+
31+
- "command":"mcp-difyworkflow-server"
32+
You can specify the absolute path for the compiled binary, or create a symbolic link with:
33+
"sudo ln -s <gitWorkPath>/mcp-difyworkflow-server /usr/local/bin/mcp-difyworkflow-server"
34+
- DIFY_WORKFLOW_NAME、DIFY_API_KEYS
35+
Need to correspond one by one in order.
36+
Workflow name can be self-defined by the user in the Prompt, and the Workflow API Key needs to be generated for the corresponding workflow created on the Dify platform (refer to the relevant Dify documentation for how to generate the API KEY).
37+
38+
## Usage:
39+
40+
- list_workflows
41+
List authorized workflows
42+
- execute_workflow
43+
Execute a specified workflow,args: workflow_name, input
44+
Note: The input variable name of the **dify workflow** should be defined by default as: **"message"**
45+
46+
- Samples:
47+
48+
> 查看 mcp-difyworkflow-server 可使用的工具列表
49+
> 给我执行 dify 名为 workflow-translator 的工作流,输入的消息为“这是一条测试消息”
50+
51+
> View the list of tools available for mcp-difyworkflow-server.
52+
> Execute the workflow named 'workflow-translator' for me, the input message is "This is a test message".

config.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"mcpServers": {
3+
"mcp-difyworkflow-server": {
4+
"command": "/Users/yyc/Documents/DevOps/mcp/mcp-difyworkflow-server/mcp-difyworkflow-server",
5+
"args": ["-base-url","http://localhost/v1"],
6+
"env": {
7+
"DIFY_WORKFLOW_NAME":"workflow-searchNews,workflow-genImag",
8+
"DIFY_API_KEYS":"apikey-xxxxxxxxxxx,apikey-xxxxxxxxxxx"
9+
}
10+
}
11+
}
12+
}

go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module mcp-difyworkflow-server
2+
3+
go 1.23.3
4+
5+
require github.com/mark3labs/mcp-go v0.8.2
6+
7+
require github.com/google/uuid v1.6.0 // indirect

go.sum

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
5+
github.com/mark3labs/mcp-go v0.8.2 h1:OtqqXlRqjXs6zuMhf1uiuQ2iqBrhMGgLpDeVDUWMKFc=
6+
github.com/mark3labs/mcp-go v0.8.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE=
7+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
8+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
10+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
11+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"os"
11+
"strings"
12+
13+
"github.com/mark3labs/mcp-go/mcp"
14+
"github.com/mark3labs/mcp-go/server"
15+
)
16+
17+
func main() {
18+
apiKeys := os.Getenv("DIFY_API_KEYS")
19+
if apiKeys == "" {
20+
fmt.Println("DIFY_API_KEYS environment variable is required")
21+
return
22+
}
23+
24+
workflowNames := os.Getenv("DIFY_WORKFLOW_NAME")
25+
if workflowNames == "" {
26+
fmt.Println("DIFY_WORKFLOW_NAME environment variable is required")
27+
return
28+
}
29+
workflows := parseAPIKeys(apiKeys, workflowNames)
30+
baseURL := flag.String("base-url", "http://localhostr/v1", "Base URL for Dify API")
31+
flag.Parse()
32+
33+
s := server.NewMCPServer(
34+
"Dify Workflow Server",
35+
"1.0.0",
36+
server.WithResourceCapabilities(true, true),
37+
server.WithLogging(),
38+
)
39+
40+
listWorkflowsTool := mcp.NewTool("list_workflows",
41+
mcp.WithDescription("List authorized workflows"),
42+
)
43+
s.AddTool(listWorkflowsTool, listWorkflowsHandler(workflows))
44+
45+
executeWorkflowTool := mcp.NewTool("execute_workflow",
46+
mcp.WithDescription("Execute a specified workflow"),
47+
mcp.WithString("workflow_name",
48+
mcp.Required(),
49+
mcp.Description("Name of the workflow to execute"),
50+
),
51+
mcp.WithString("input",
52+
mcp.Description("Input data for the workflow"),
53+
),
54+
)
55+
s.AddTool(executeWorkflowTool, executeWorkflowHandler(workflows, *baseURL))
56+
57+
if err := server.ServeStdio(s); err != nil {
58+
fmt.Printf("Server error: %v\n", err)
59+
}
60+
}
61+
62+
func parseAPIKeys(apiKeys, workflowNames string) map[string]string {
63+
workflows := make(map[string]string)
64+
65+
apiKeyList := strings.Split(apiKeys, ",")
66+
workflowNameList := strings.Split(workflowNames, ",")
67+
68+
if len(apiKeyList) != len(workflowNameList) {
69+
fmt.Printf("The number of API Keys does not match the number of workflow names\n")
70+
os.Exit(1)
71+
}
72+
73+
for i, name := range workflowNameList {
74+
workflows[name] = apiKeyList[i]
75+
}
76+
77+
return workflows
78+
}
79+
80+
func listWorkflowsHandler(workflows map[string]string) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
81+
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
82+
workflowNames := make([]string, 0, len(workflows))
83+
for name := range workflows {
84+
workflowNames = append(workflowNames, name)
85+
}
86+
return mcp.NewToolResultText(fmt.Sprintf("Authorized workflows: %v", workflowNames)), nil
87+
}
88+
}
89+
90+
func executeWorkflowHandler(workflows map[string]string, baseURL string) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
91+
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
92+
workflowName := request.Params.Arguments["workflow_name"].(string)
93+
apiKey, ok := workflows[workflowName]
94+
if !ok {
95+
return mcp.NewToolResultError(fmt.Sprintf("Workflow %s not found", workflowName)), nil
96+
}
97+
98+
input := ""
99+
if in, ok := request.Params.Arguments["input"].(string); ok {
100+
input = in
101+
}
102+
103+
response, err := callDifyWorkflowAPI(baseURL, apiKey, input)
104+
if err != nil {
105+
return mcp.NewToolResultError(fmt.Sprintf("Failed to execute workflow: %v", err)), nil
106+
}
107+
108+
return mcp.NewToolResultText(response), nil
109+
}
110+
}
111+
112+
func callDifyWorkflowAPI(baseURL, apiKey, input string) (string, error) {
113+
client := &http.Client{}
114+
115+
body := fmt.Sprintf(`{
116+
"inputs": {"message":"%s"},
117+
"response_mode": "blocking",
118+
"user": "mcp-server"
119+
}`, input)
120+
121+
req, err := http.NewRequest("POST", fmt.Sprintf("%s/workflows/run", baseURL), strings.NewReader(body))
122+
if err != nil {
123+
return "", err
124+
}
125+
126+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
127+
req.Header.Set("Content-Type", "application/json")
128+
129+
resp, err := client.Do(req)
130+
if err != nil {
131+
return "", err
132+
}
133+
defer resp.Body.Close()
134+
135+
respBody, err := ioutil.ReadAll(resp.Body)
136+
if err != nil {
137+
return "", err
138+
}
139+
140+
var completionResponse DifyResponse
141+
if err := json.Unmarshal(respBody, &completionResponse); err != nil {
142+
return "", err
143+
}
144+
145+
if completionResponse.Data.Status == "succeeded" {
146+
return fmt.Sprintf("%s", completionResponse.Data.Outputs["text"]), nil
147+
}
148+
149+
return "", fmt.Errorf("Workflow execution failed: %s", respBody)
150+
}

0 commit comments

Comments
 (0)