开启左侧

基于golang 实现dify的MCP SSE服务器

[复制链接]
作者:CSDN博客
1 服务器使用代码
  1. package main
  2. import (
  3.         "context"
  4.         "encoding/json"
  5.         "fmt"
  6.         "github.com/mark3labs/mcp-go/mcp"
  7.         "github.com/mark3labs/mcp-go/server"
  8.         "io"
  9.         "log"
  10.         "net/http"
  11.         "os"
  12. )
  13. func main() {
  14.         // Create a new MCP server
  15.         s := server.NewMCPServer(
  16.                 "Calculator Demo",
  17.                 "1.0.0",
  18.                 server.WithResourceCapabilities(true, true),
  19.                 server.WithLogging(),
  20.                 server.WithRecovery(),
  21.         )
  22.         // 实现tools 工具
  23.         calculatorTool := mcp.NewTool("calculate",
  24.                 mcp.WithDescription("Perform basic arithmetic operations"),
  25.                 mcp.WithString("operation",
  26.                         mcp.Required(),
  27.                         mcp.Description("The operation to perform (add, subtract, multiply, divide)"),
  28.                         mcp.Enum("add", "subtract", "multiply", "divide"),
  29.                 ),
  30.                 mcp.WithNumber("x",
  31.                         mcp.Required(),
  32.                         mcp.Description("First number"),
  33.                 ),
  34.                 mcp.WithNumber("y",
  35.                         mcp.Required(),
  36.                         mcp.Description("Second number"),
  37.                 ),
  38.         )
  39.         //资源
  40.         s.AddResource(mcp.NewResource("test://static/resource",
  41.                 "Static Resource",
  42.                 mcp.WithMIMEType("text/plain"),
  43.         ), handleReadResource)
  44.         //动态模板资源
  45.         s.AddResourceTemplate(
  46.                 mcp.NewResourceTemplate(
  47.                         "test://dynamic/resource/{id}",
  48.                         "Dynamic Resource",
  49.                 ),
  50.                 handleResourceTemplate,
  51.         )
  52.         // Add the calculator handler
  53.         s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  54.                 op := request.Params.Arguments["operation"].(string)
  55.                 x := request.Params.Arguments["x"].(float64)
  56.                 y := request.Params.Arguments["y"].(float64)
  57.                 var result float64
  58.                 switch op {
  59.                 case "add":
  60.                         result = x + y
  61.                 case "subtract":
  62.                         result = x - y
  63.                 case "multiply":
  64.                         result = x * y
  65.                 case "divide":
  66.                         if y == 0 {
  67.                                 return mcp.NewToolResultError("cannot divide by zero"), nil
  68.                         }
  69.                         result = x / y
  70.                 }
  71.                 return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil
  72.         })
  73.         sseServer := server.NewSSEServer(s,
  74.                 server.WithSSEContextFunc(authFromRequest),
  75.         )
  76.         if err := sseServer.Start(":8080"); err != nil {
  77.                 log.Fatalf("Server error: %v", err)
  78.         }
  79. }
  80. func handleResourceTemplate(
  81.         ctx context.Context,
  82.         request mcp.ReadResourceRequest,
  83. ) ([]mcp.ResourceContents, error) {
  84.         return []mcp.ResourceContents{
  85.                 mcp.TextResourceContents{
  86.                         URI:      request.Params.URI,
  87.                         MIMEType: "text/plain",
  88.                         Text:     fmt.Sprintf("动态模板:%+v ;", request),
  89.                 },
  90.         }, nil
  91. }
  92. func handleReadResource(
  93.         ctx context.Context,
  94.         request mcp.ReadResourceRequest,
  95. ) ([]mcp.ResourceContents, error) {
  96.         return []mcp.ResourceContents{
  97.                 mcp.TextResourceContents{
  98.                         URI:      "test://static/resource",
  99.                         MIMEType: "text/plain",
  100.                         Text:     fmt.Sprintf("模板 %+v ;", request),
  101.                 },
  102.         }, nil
  103. }
  104. // authKey is a custom context key for storing the auth token.
  105. type authKey struct{}
  106. // withAuthKey adds an auth key to the context.
  107. func withAuthKey(ctx context.Context, auth string) context.Context {
  108.         return context.WithValue(ctx, authKey{}, auth)
  109. }
  110. // authFromRequest extracts the auth token from the request headers.
  111. func authFromRequest(ctx context.Context, r *http.Request) context.Context {
  112.         return withAuthKey(ctx, r.Header.Get("Authorization"))
  113. }
  114. // authFromEnv extracts the auth token from the environment
  115. func authFromEnv(ctx context.Context) context.Context {
  116.         return withAuthKey(ctx, os.Getenv("API_KEY"))
  117. }
  118. // tokenFromContext extracts the auth token from the context.
  119. // This can be used by tools to extract the token regardless of the
  120. // transport being used by the server.
  121. func tokenFromContext(ctx context.Context) (string, error) {
  122.         auth, ok := ctx.Value(authKey{}).(string)
  123.         if !ok {
  124.                 return "", fmt.Errorf("missing auth")
  125.         }
  126.         return auth, nil
  127. }
  128. type response struct {
  129.         Args    map[string]interface{} `json:"args"`
  130.         Headers map[string]string      `json:"headers"`
  131. }
  132. // makeRequest makes a request to httpbin.org including the auth token in the request
  133. // headers and the message in the query string.
  134. func makeRequest(ctx context.Context, message, token string) (*response, error) {
  135.         req, err := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/anything", nil)
  136.         if err != nil {
  137.                 return nil, err
  138.         }
  139.         req.Header.Set("Authorization", token)
  140.         query := req.URL.Query()
  141.         query.Add("message", message)
  142.         req.URL.RawQuery = query.Encode()
  143.         resp, err := http.DefaultClient.Do(req)
  144.         if err != nil {
  145.                 return nil, err
  146.         }
  147.         defer resp.Body.Close()
  148.         body, err := io.ReadAll(resp.Body)
  149.         if err != nil {
  150.                 return nil, err
  151.         }
  152.         var r *response
  153.         if err := json.Unmarshal(body, &r); err != nil {
  154.                 return nil, err
  155.         }
  156.         return r, nil
  157. }
  158. // handleMakeAuthenticatedRequestTool is a tool that makes an authenticated request
  159. // using the token from the context.
  160. func handleMakeAuthenticatedRequestTool(
  161.         ctx context.Context,
  162.         request mcp.CallToolRequest,
  163. ) (*mcp.CallToolResult, error) {
  164.         message, ok := request.Params.Arguments["message"].(string)
  165.         if !ok {
  166.                 return nil, fmt.Errorf("missing message")
  167.         }
  168.         token, err := tokenFromContext(ctx)
  169.         if err != nil {
  170.                 return nil, fmt.Errorf("missing token: %v", err)
  171.         }
  172.         // Now our tool can make a request with the token, irrespective of where it came from.
  173.         resp, err := makeRequest(ctx, message, token)
  174.         if err != nil {
  175.                 return nil, err
  176.         }
  177.         return mcp.NewToolResultText(fmt.Sprintf("%+v", resp)), nil
  178. }
复制代码
go mod tidy
运行服务
2 使用MCP Inspector 测试连接服务器
2.1 安装Inspector(没有nodejs 请先安装)
  1. npx @modelcontextprotocol/inspector
复制代码
2.2测试链接
基于golang 实现dify的MCP SSE服务器-1.png


基于golang 实现dify的MCP SSE服务器-2.png


基于golang 实现dify的MCP SSE服务器-3.png


已连接
3 在dify下载以下以下插件
基于golang 实现dify的MCP SSE服务器-4.png


配置MCP SSE授权
  1. {  "server_name": {    "url": "http://host.docker.internal:8080/sse",    "headers": {}, "timeout": 60,    "sse_read_timeout": 300  }}
复制代码
因为我的 dify是在docker中运行,mcp是在宿主机运行的所以配置 http://host.docker.internal:8080/sse,其他请注意修改正确的服务地址
基于golang 实现dify的MCP SSE服务器-5.png



原文地址:https://blog.csdn.net/weixin_40292098/article/details/147394431
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题
阅读排行更多+

Powered by Discuz! X3.4© 2001-2013 Discuz Team.( 京ICP备17022993号-3 )