首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >golang实现mcp server

golang实现mcp server

作者头像
golangLeetcode
发布2026-03-18 16:58:49
发布2026-03-18 16:58:49
2300
举报

mcp(Model Context Protocol)是llm和外部交互的接口协议,由两部分组成mcp server和mcp client;mcp server 作为资源或者工具的提供者,暴露资源接口。mcp client通过json rpc协议访问 mcp server的资源。像cursor本身就是一个模版化的mcp client通过json配置文件实现和适配不同的mcp client。MCP 有以下几个核心功能:

• Resources 资源

• Prompts 提示词

• Tools 工具

• Sampling 采样

• Roots 根目录

• Transports 传输层

其中 MCP 的传输层支持了 2 种协议的实现:stdio(标准输入/输出)和 SSE(服务器发送事件)

下面我们基于stdio实现一个简单的mcp server,实现依赖库github.com/mark3labs/mcp-go

代码语言:javascript
复制
package main
import (
    "context"
    "errors"
    "fmt"

    "github.com/mark3labs/mcp-go/server"
)
func main() {
    // Create MCP server
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
    )
    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

这就是一个没有提供任何资源或者工具的mcp server。首先定义了server 结构体,然后启动Stdio server,它类似于网络服务器,不同的是网络服务器提供基于网络连接的io,而 stdio server,提供基于标准输入输出io的操作。

当然我们也可以声明一个tool,并且提供对应的handler

代码语言:javascript
复制
package main
import (
    "context"
    "errors"
    "fmt"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)
func main() {
    // Create MCP server
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
    )
    // Add tool
    tool := mcp.NewTool("hello_world",
        mcp.WithDescription("Say hello to someone"),
        mcp.WithString("name",
            mcp.Required(),
            mcp.Description("Name of the person to greet"),
        ),
    )
    // Add tool handler
    s.AddTool(tool, helloHandler)
    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}
func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    name, ok := request.Params.Arguments["name"].(string)
    if !ok {
        return nil, errors.New("name must be a string")
    }
    return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

当然也可以声明复杂的tool,比如发起http请求

代码语言:javascript
复制
package main
import (
    "context"
    "fmt"
    "io"
    "net/http"
    "strings"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)
func main() {
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
    )
    httpTool := mcp.NewTool("http_request",
        mcp.WithDescription("Make HTTP requests to external APIs"),
        mcp.WithString("method",
            mcp.Required(),
            mcp.Description("HTTP method to use"),
            mcp.Enum("GET", "POST", "PUT", "DELETE"),
        ),
        mcp.WithString("url",
            mcp.Required(),
            mcp.Description("URL to send the request to"),
            mcp.Pattern("^https?://.*"),
        ),
        mcp.WithString("body",
            mcp.Description("Request body (for POST/PUT)"),
        ),
    )
    s.AddTool(httpTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        method := request.Params.Arguments["method"].(string)
        url := request.Params.Arguments["url"].(string)
        body := ""
        if b, ok := request.Params.Arguments["body"].(string); ok {
            body = b
        }
        // Create and send request
        var req *http.Request
        var err error
        if body != "" {
            req, err = http.NewRequest(method, url, strings.NewReader(body))
        } else {
            req, err = http.NewRequest(method, url, nil)
        }
        if err != nil {
            return nil, fmt.Errorf("Failed to create request: %v", err)
        }
        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            return nil, fmt.Errorf("Request failed: %v", err)
        }
        defer resp.Body.Close()
        // Return response
        respBody, err := io.ReadAll(resp.Body)
        if err != nil {
            return nil, fmt.Errorf("Failed to read response: %v", err)
        }
        return mcp.NewToolResultText(fmt.Sprintf("Status: %d\nBody: %s", resp.StatusCode, string(respBody))), nil
    })
    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

也可以声明资源,具体例子如下:

代码语言:javascript
复制
package main
import (
    "context"
    "fmt"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)
func main() {
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
    )
    // Dynamic resource example - user profiles by ID
    template := mcp.NewResourceTemplate(
        "users://{id}/profile",
        "User Profile",
        mcp.WithTemplateDescription("Returns user profile information"),
        mcp.WithTemplateMIMEType("application/json"),
    )
    // Add template with its handler
    s.AddResourceTemplate(template, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
        // Extract ID from the URI using regex matching
        // The server automatically matches URIs to templates
        userID := extractIDFromURI(request.Params.URI)
        profile, err := getUserProfile(userID) // Your DB/API call here
        if err != nil {
            return nil, err
        }
        return []mcp.ResourceContents{
            mcp.TextResourceContents{
                URI:      request.Params.URI,
                MIMEType: "application/json",
                Text:     profile,
            },
        }, nil
    })
    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}
func extractIDFromURI(uri string) string {
    // Your regex matching code here
    return "1234"
}
func getUserProfile(userID string) (string, error) {
    // Your DB/API call here
    return `{"name": "John Doe", "email": "john.doe@example.com"}`, nil
}

也可以声明提示词

代码语言:javascript
复制
package main
import (
    "context"
    "fmt"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)
func main() {
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
    )
    // Simple greeting prompt
    s.AddPrompt(mcp.NewPrompt("greeting",
        mcp.WithPromptDescription("A friendly greeting prompt"),
        mcp.WithArgument("name",
            mcp.ArgumentDescription("Name of the person to greet"),
        ),
    ), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
        name := request.Params.Arguments["name"]
        if name == "" {
            name = "friend"
        }
        return mcp.NewGetPromptResult(
            "A friendly greeting",
            []mcp.PromptMessage{
                mcp.NewPromptMessage(
                    mcp.RoleAssistant,
                    mcp.NewTextContent(fmt.Sprintf("Hello, %s! How can I help you today?", name)),
                ),
            },
        ), nil
    })
    // Code review prompt with embedded resource
    s.AddPrompt(mcp.NewPrompt("code_review",
        mcp.WithPromptDescription("Code review assistance"),
        mcp.WithArgument("pr_number",
            mcp.ArgumentDescription("Pull request number to review"),
            mcp.RequiredArgument(),
        ),
    ), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
        prNumber := request.Params.Arguments["pr_number"]
        if prNumber == "" {
            return nil, fmt.Errorf("pr_number is required")
        }
        return mcp.NewGetPromptResult(
            "Code review assistance",
            []mcp.PromptMessage{
                mcp.NewPromptMessage(
                    //mcp.RoleSystem,
                    mcp.RoleAssistant,
                    mcp.NewTextContent("You are a helpful code reviewer. Review the changes and provide constructive feedback."),
                ),
                mcp.NewPromptMessage(
                    mcp.RoleAssistant,
                    mcp.NewEmbeddedResource(mcp.TextResourceContents{
                        URI:      fmt.Sprintf("git://pulls/%s/diff", prNumber),
                        MIMEType: "text/x-diff",
                    }),
                ),
            },
        ), nil
    })
    // Database query builder prompt
    s.AddPrompt(mcp.NewPrompt("query_builder",
        mcp.WithPromptDescription("SQL query builder assistance"),
        mcp.WithArgument("table",
            mcp.ArgumentDescription("Name of the table to query"),
            mcp.RequiredArgument(),
        ),
    ), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
        tableName := request.Params.Arguments["table"]
        if tableName == "" {
            return nil, fmt.Errorf("table name is required")
        }
        return mcp.NewGetPromptResult(
            "SQL query builder assistance",
            []mcp.PromptMessage{
                mcp.NewPromptMessage(
                    // mcp.RoleSystem,
                    mcp.RoleAssistant,
                    mcp.NewTextContent("You are a SQL expert. Help construct efficient and safe queries."),
                ),
                mcp.NewPromptMessage(
                    mcp.RoleAssistant,
                    mcp.NewEmbeddedResource(mcp.TextResourceContents{
                        URI:      fmt.Sprintf("db://schema/%s", tableName),
                        MIMEType: "application/json",
                    }),
                ),
            },
        ), nil
    })
    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

其实理解了mcp的真正含义后,我们就可以非常方便地声明各种需要的mcp server。声明了mcp server后我们应该如何使用它呢?我们看下一篇详细介绍。

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

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

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

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

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