首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Dify mcp

Dify mcp

作者头像
golangLeetcode
发布2026-03-18 17:32:18
发布2026-03-18 17:32:18
1470
举报

随着MCP协议爆火,Dify也增加了支持mcp的插件,本篇分为两个方面来介绍Dify mcp,首先是Dify通过mcp协议调用本地实现的mcp server;然后是Dify把自己的aget 或者流水线封装起来作为mcp server给外界使用。实现这些功能,需要在dify基础上有一个适配层,这个就是Dify的mcp插件。目前比较熟知的有下面四个插件,Mcp Agent策略、Agent 策略、MCP SSE 和mcp-server,其中前三个是调用外界mcp server的,最后一个是把Dify的能力封装成mcp server给外界使用的。下面我们重点介绍下MCP SSE和mcp-server

首先我们在Dify的插件市场安装上述插件,然后开发一个mcp server

代码语言:javascript
复制
package main
import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)
func main() {
    // Create a new MCP server
    s := server.NewMCPServer(
        "Calculator Demo",
        "1.0.0",
        server.WithResourceCapabilities(true, true),
        server.WithLogging(),
        server.WithRecovery(),
    )
    // 实现tools 工具
    calculatorTool := mcp.NewTool("calculate",
        mcp.WithDescription("Perform basic arithmetic operations"),
        mcp.WithString("operation",
            mcp.Required(),
            mcp.Description("The operation to perform (add, subtract, multiply, divide)"),
            mcp.Enum("add", "subtract", "multiply", "divide"),
        ),
        mcp.WithNumber("x",
            mcp.Required(),
            mcp.Description("First number"),
        ),
        mcp.WithNumber("y",
            mcp.Required(),
            mcp.Description("Second number"),
        ),
    )
    //资源
    s.AddResource(mcp.NewResource("test://static/resource",
        "Static Resource",
        mcp.WithMIMEType("text/plain"),
    ), handleReadResource)
    //动态模板资源
    s.AddResourceTemplate(
        mcp.NewResourceTemplate(
            "test://dynamic/resource/{id}",
            "Dynamic Resource",
        ),
        handleResourceTemplate,
    )
    // Add the calculator handler
    s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        op := request.Params.Arguments["operation"].(string)
        x := request.Params.Arguments["x"].(float64)
        y := request.Params.Arguments["y"].(float64)
        var result float64
        switch op {
        case "add":
            result = x + y
        case "subtract":
            result = x - y
        case "multiply":
            result = x * y
        case "divide":
            if y == 0 {
                return mcp.NewToolResultError("cannot divide by zero"), nil
            }
            result = x / y
        }
        fmt.Println("call mcp tool success")
        return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil
    })
    sseServer := server.NewSSEServer(s,
        server.WithSSEContextFunc(authFromRequest),
    )
    if err := sseServer.Start(":8080"); err != nil {
        log.Fatalf("Server error: %v", err)
    }
}
func handleResourceTemplate(
    ctx context.Context,
    request mcp.ReadResourceRequest,
) ([]mcp.ResourceContents, error) {
    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      request.Params.URI,
            MIMEType: "text/plain",
            Text:     fmt.Sprintf("动态模板:%+v ;", request),
        },
    }, nil
}
func handleReadResource(
    ctx context.Context,
    request mcp.ReadResourceRequest,
) ([]mcp.ResourceContents, error) {
    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      "test://static/resource",
            MIMEType: "text/plain",
            Text:     fmt.Sprintf("模板 %+v ;", request),
        },
    }, nil
}
// authKey is a custom context key for storing the auth token.
type authKey struct{}
// withAuthKey adds an auth key to the context.
func withAuthKey(ctx context.Context, auth string) context.Context {
    return context.WithValue(ctx, authKey{}, auth)
}
// authFromRequest extracts the auth token from the request headers.
func authFromRequest(ctx context.Context, r *http.Request) context.Context {
    return withAuthKey(ctx, r.Header.Get("Authorization"))
}
// authFromEnv extracts the auth token from the environment
func authFromEnv(ctx context.Context) context.Context {
    return withAuthKey(ctx, os.Getenv("API_KEY"))
}
// tokenFromContext extracts the auth token from the context.
// This can be used by tools to extract the token regardless of the
// transport being used by the server.
func tokenFromContext(ctx context.Context) (string, error) {
    auth, ok := ctx.Value(authKey{}).(string)
    if !ok {
        return "", fmt.Errorf("missing auth")
    }
    return auth, nil
}
type response struct {
    Args    map[string]interface{} `json:"args"`
    Headers map[string]string      `json:"headers"`
}
// makeRequest makes a request to httpbin.org including the auth token in the request
// headers and the message in the query string.
func makeRequest(ctx context.Context, message, token string) (*response, error) {
    req, err := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/anything", nil)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Authorization", token)
    query := req.URL.Query()
    query.Add("message", message)
    req.URL.RawQuery = query.Encode()
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    var r *response
    if err := json.Unmarshal(body, &r); err != nil {
        return nil, err
    }
    return r, nil
}
// handleMakeAuthenticatedRequestTool is a tool that makes an authenticated request
// using the token from the context.
func handleMakeAuthenticatedRequestTool(
    ctx context.Context,
    request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
    message, ok := request.Params.Arguments["message"].(string)
    if !ok {
        return nil, fmt.Errorf("missing message")
    }
    token, err := tokenFromContext(ctx)
    if err != nil {
        return nil, fmt.Errorf("missing token: %v", err)
    }
    // Now our tool can make a request with the token, irrespective of where it came from.
    resp, err := makeRequest(ctx, message, token)
    if err != nil {
        return nil, err
    }
    return mcp.NewToolResultText(fmt.Sprintf("%+v", resp)), nil
}

为了验证我们mcp server的可用性,我们使用工具来发现和使用下这个mcp sse server

代码语言:javascript
复制
npx @modelcontextprotocol/inspector
Need to install the following packages:
@modelcontextprotocol/inspector
Ok to proceed? (y) y

需要注意的是这个包,需要依赖18.14以上的node

https://github.com/modelcontextprotocol/inspector

代码语言:javascript
复制
nvm use v18.14.0

安装成功后如下

代码语言:javascript
复制
% npx @modelcontextprotocol/inspector
Starting MCP inspector...
⚙️ Proxy server listening on port 6277
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀

启动我们的mcp server后,工具就可以发现我们的mcp server,然后就可以使用下

安装完 mcp sse插件之后,我们点击插件,在插件上可以配置我们的mcp server的地址

https://github.com/junjiem/dify-plugin-tools-mcp_sse

注意需要配置为http://host.docker.internal:8080/sse,因为docker for mac。配置成功后的效果如下

然后在我们的聊天工具里尝试使用下mcp,需要注意的是DeepSeek1.5b不支持mcp,这里大模型选智普AI,实验结果如下

至此,Dify调用外界的mcp server 的流程介绍完毕,这也是我们多数场景需要使用的情况。如果我们在Dify中集成了特别厉害的功能,想在本地工具中使用,这个如何操作呢,可以使用mcp-server插件,将Dify的功能作为一个mcp server发布给外界使用。

配置完成后我们得到了链接,需要注意的是,这个插件有bug,这里的

代码语言:javascript
复制
http://localhost/e/fos7gt47jnofl9db}/mcp
代码语言:javascript
复制
http://localhost/e/fos7gt47jnofl9db/mcp

链接多了个}导致我们调用失败。然后我们使用mcp客户端工具调用下测试。https://github.com/CherryHQ/cherry-studio V1.2.9 及以上版本以获得更稳定的 MCP 功能支持。配置界面如下,如果能保存成功,说明已经调试成功了

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档