增加供应商智能添加功能
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
// 内置演示 MCP 服务(stdio 传输)
|
||||
// 提供 get_time / calculate / weather_query / echo 等工具,用于验证「MCP 市场 → 会话工具调用」全链路。
|
||||
// 运行:go run ./cmd/demo-mcp 或直接运行已编译二进制 bin/demo-mcp
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
func toolResultText(text string) *mcp.CallToolResult {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{Type: "text", Text: text},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
srv := server.NewMCPServer("demo-mcp", "1.0.0")
|
||||
|
||||
// 1. 当前时间
|
||||
srv.AddTool(mcp.NewTool(
|
||||
"get_time",
|
||||
mcp.WithDescription("获取服务器当前日期和时间(含星期),不需要参数"),
|
||||
), func(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
now := time.Now()
|
||||
weekdays := []string{"日", "一", "二", "三", "四", "五", "六"}
|
||||
return toolResultText(fmt.Sprintf("当前时间:%s(星期%s)",
|
||||
now.Format("2006-01-02 15:04:05"), weekdays[int(now.Weekday())])), nil
|
||||
})
|
||||
|
||||
// 2. 计算器
|
||||
srv.AddTool(mcp.NewTool(
|
||||
"calculate",
|
||||
mcp.WithDescription("计算一个数学表达式,支持 + - * / % 与括号。例如:2+3*4"),
|
||||
mcp.WithString("expression", mcp.Required(), mcp.Description("数学表达式字符串")),
|
||||
), func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
expr := req.GetArguments()["expression"]
|
||||
exprStr, _ := expr.(string)
|
||||
exprStr = strings.TrimSpace(exprStr)
|
||||
if exprStr == "" {
|
||||
return toolResultText("错误:expression 参数不能为空"), nil
|
||||
}
|
||||
val, err := safeEval(exprStr)
|
||||
if err != nil {
|
||||
return toolResultText("错误:" + err.Error()), nil
|
||||
}
|
||||
return toolResultText(fmt.Sprintf("%s = %v", exprStr, val)), nil
|
||||
})
|
||||
|
||||
// 3. 模拟天气
|
||||
srv.AddTool(mcp.NewTool(
|
||||
"weather_query",
|
||||
mcp.WithDescription("查询指定城市的模拟天气信息(演示数据,非真实天气)"),
|
||||
mcp.WithString("city", mcp.Required(), mcp.Description("城市名称,如 北京、上海、连云港")),
|
||||
), func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
city, _ := req.GetArguments()["city"].(string)
|
||||
city = strings.TrimSpace(city)
|
||||
if city == "" {
|
||||
city = "未知城市"
|
||||
}
|
||||
conditions := []string{"晴", "多云", "小雨", "阴", "晴转多云"}
|
||||
cond := conditions[int(time.Now().Unix())%len(conditions)]
|
||||
temp := 18 + int(time.Now().Unix())%15
|
||||
return toolResultText(fmt.Sprintf("【%s】今天 %s,气温 %d℃,空气质量:良(模拟数据)", city, cond, temp)), nil
|
||||
})
|
||||
|
||||
// 4. 回声
|
||||
srv.AddTool(mcp.NewTool(
|
||||
"echo",
|
||||
mcp.WithDescription("原样返回你输入的文字,用于测试"),
|
||||
mcp.WithString("text", mcp.Required(), mcp.Description("要回显的文字")),
|
||||
), func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
text, _ := req.GetArguments()["text"].(string)
|
||||
return toolResultText("echo: " + text), nil
|
||||
})
|
||||
|
||||
// 5. 获取用户信息(演示带对象参数的 JSON 参数)
|
||||
srv.AddTool(mcp.NewTool(
|
||||
"get_user_profile",
|
||||
mcp.WithDescription("获取用户画像信息(演示),可指定姓名与年龄"),
|
||||
mcp.WithString("name", mcp.Required(), mcp.Description("姓名")),
|
||||
mcp.WithNumber("age", mcp.Description("年龄")),
|
||||
), func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := req.GetArguments()
|
||||
name, _ := args["name"].(string)
|
||||
age, _ := args["age"].(float64)
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"name": name,
|
||||
"age": int(age),
|
||||
"role": "演示用户",
|
||||
"note": "这是一条来自内置演示 MCP 服务的测试数据",
|
||||
})
|
||||
return toolResultText(string(out)), nil
|
||||
})
|
||||
|
||||
if err := server.ServeStdio(srv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// safeEval 极简安全表达式求值(仅支持数字与 + - * / % 和括号)
|
||||
func safeEval(expr string) (float64, error) {
|
||||
parser := &exprParser{s: expr}
|
||||
parser.skipSpace()
|
||||
v, err := parser.parseExpr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
parser.skipSpace()
|
||||
if parser.pos < len(parser.s) {
|
||||
return 0, fmt.Errorf("无法解析的表达式(非法字符)")
|
||||
}
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return 0, fmt.Errorf("计算结果无效")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type exprParser struct {
|
||||
s string
|
||||
pos int
|
||||
}
|
||||
|
||||
func (p *exprParser) skipSpace() {
|
||||
for p.pos < len(p.s) && (p.s[p.pos] == ' ' || p.s[p.pos] == '\t') {
|
||||
p.pos++
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exprParser) peek() byte {
|
||||
if p.pos < len(p.s) {
|
||||
return p.s[p.pos]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *exprParser) parseExpr() (float64, error) {
|
||||
v, err := p.parseTerm()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for {
|
||||
p.skipSpace()
|
||||
c := p.peek()
|
||||
if c == '+' || c == '-' {
|
||||
p.pos++
|
||||
rhs, err := p.parseTerm()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if c == '+' {
|
||||
v += rhs
|
||||
} else {
|
||||
v -= rhs
|
||||
}
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exprParser) parseTerm() (float64, error) {
|
||||
v, err := p.parseFactor()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for {
|
||||
p.skipSpace()
|
||||
c := p.peek()
|
||||
if c == '*' || c == '/' || c == '%' {
|
||||
p.pos++
|
||||
rhs, err := p.parseFactor()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch c {
|
||||
case '*':
|
||||
v *= rhs
|
||||
case '/':
|
||||
if rhs == 0 {
|
||||
return 0, fmt.Errorf("除数不能为 0")
|
||||
}
|
||||
v /= rhs
|
||||
case '%':
|
||||
if rhs == 0 {
|
||||
return 0, fmt.Errorf("取模除数不能为 0")
|
||||
}
|
||||
v = math.Mod(v, rhs)
|
||||
}
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exprParser) parseFactor() (float64, error) {
|
||||
p.skipSpace()
|
||||
c := p.peek()
|
||||
if c == '(' {
|
||||
p.pos++
|
||||
v, err := p.parseExpr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
p.skipSpace()
|
||||
if p.peek() != ')' {
|
||||
return 0, fmt.Errorf("缺少右括号")
|
||||
}
|
||||
p.pos++
|
||||
return v, nil
|
||||
}
|
||||
return p.parseNumber()
|
||||
}
|
||||
|
||||
func (p *exprParser) parseNumber() (float64, error) {
|
||||
start := p.pos
|
||||
for p.pos < len(p.s) {
|
||||
c := p.s[p.pos]
|
||||
if (c >= '0' && c <= '9') || c == '.' {
|
||||
p.pos++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if start == p.pos {
|
||||
return 0, fmt.Errorf("表达式格式错误(无法识别的字符)")
|
||||
}
|
||||
raw := p.s[start:p.pos]
|
||||
var v float64
|
||||
if _, err := fmt.Sscanf(raw, "%g", &v); err != nil {
|
||||
return 0, fmt.Errorf("数字格式错误: %s", raw)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
Reference in New Issue
Block a user