AI创想
标题:
基于golang 实现dify的MCP SSE服务器
[打印本页]
作者:
90后的奋斗0323
时间:
昨天 23:15
标题:
基于golang 实现dify的MCP SSE服务器
作者:CSDN博客
1 服务器使用代码
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"io"
"log"
"net/http"
"os"
)
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
}
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
}
复制代码
go mod tidy
运行服务
2 使用MCP Inspector 测试连接服务器
2.1 安装Inspector(没有nodejs 请先安装)
npx @modelcontextprotocol/inspector
复制代码
2.2测试链接
(, 下载次数: 0)
上传
点击文件名下载附件
(, 下载次数: 0)
上传
点击文件名下载附件
(, 下载次数: 0)
上传
点击文件名下载附件
已连接
3 在dify下载以下以下插件
(, 下载次数: 0)
上传
点击文件名下载附件
配置MCP SSE授权
{ "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