AI创想

标题: 基于golang 实现dify的MCP SSE服务器 [打印本页]

作者: 90后的奋斗0323    时间: 昨天 23:15
标题: 基于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测试链接
(, 下载次数: 0)


(, 下载次数: 0)


(, 下载次数: 0)


已连接
3 在dify下载以下以下插件
(, 下载次数: 0)


配置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,其他请注意修改正确的服务地址
(, 下载次数: 0)



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




欢迎光临 AI创想 (https://www.llms-ai.com/) Powered by Discuz! X3.4