mcpField
M
MCP Go 🚀

MCP Go 🚀

本地服务
mcp-go.dev开发工具9.2k今天更新

关于 MCP Go 🚀

MCP Go 是 Model Context Protocol 的 Go 语言实现,用于连接 LLM 应用与外部数据源和工具。项目面向 Go 开发者,目标是以较少样板代码构建 MCP 服务,目前仍在积极开发中。

主要功能

  1. 01高层接口,减少样板代码
  2. 02支持 Resources、Tools 与 Prompts
  3. 03支持 stdio、SSE 与 streamable-HTTP 传输
  4. 04支持异步任务工具与并发任务上限
  5. 05提供 OAuth 资源元数据与 CORS 配置

适用场景

  1. 01用 Go 构建暴露工具与数据的 MCP 服务
  2. 02为浏览器端 MCP 客户端配置 CORS 与 HTTP 传输
  3. 03构建需要长时间运行的异步工具

MCP 相关

MCP Go 是用于构建 MCP 服务的 Go SDK,可将 Resources、Tools 与 Prompts 暴露给 MCP 客户端。README 注明协议实现仍在完善中。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

MCP Go 标志

Build Go Report Card GoDoc

AgentRank Model Context Protocol(MCP)的 Go 实现,可让大模型应用与外部数据源和工具无缝集成。


教程


在 Discord 上讨论 SDK

package main

import (
    "context"
    "fmt"

    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    // Create a new MCP server
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
        server.WithToolCapabilities(false),
    )

    // 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, err := request.RequireString("name")
    if err != nil {
        return mcp.NewToolResultError(err.Error()), nil
    }

    return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

就是这么简单!

MCP Go 负责处理所有复杂的协议细节和服务管理,让你专注于构建出色的工具。它致力于提供高级、易用的接口。

主要功能:
  • 快速:高级接口意味着代码更少、开发更快
  • 简单:只需极少的样板代码即可构建 MCP 服务
  • 完整*:MCP Go 致力于完整实现核心 MCP 规范

(*强调“致力于”)

🚨 🚧 🏗️ MCP Go 正在积极开发中,MCP 规范本身也在不断演进。核心功能已可使用,但部分高级功能仍在开发中。

目录

安装

go get github.com/mark3labs/mcp-go

快速入门

让我们创建一个简单的 MCP 服务,公开一个计算器工具和一些数据:

package main

import (
    "context"
    "fmt"

    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    // Create a new MCP server
    s := server.NewMCPServer(
        "Calculator Demo",
        "1.0.0",
        server.WithToolCapabilities(false),
        server.WithRecovery(),
    )

    // Add a calculator tool
    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"),
        ),
    )

    // Add the calculator handler
    s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        // Using helper functions for type-safe argument access
        op, err := request.RequireString("operation")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }
        
        x, err := request.RequireFloat("x")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }
        
        y, err := request.RequireFloat("y")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }

        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
    })

    // Start the server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

什么是 MCP?

Model Context Protocol(MCP) 让你能够构建以安全、标准化方式向大模型应用公开数据和功能的服务。可以把它看作 Web API,但它专为大模型交互而设计。

MCP 服务可以:

  • 通过资源公开数据(可以把它们想象成 GET 端点;它们用于将信息加载到大模型的上下文中)
  • 通过工具提供功能(可以把它们想象成 POST 端点;它们用于执行代码或以其他方式产生副作用)
  • 通过提示词定义交互模式(用于大模型交互的可复用模板)
  • 还有更多!

mcp-go 实现了版本 2025-11-25 的 Model Context Protocol 规范,并向后兼容版本 2025-06-18、2025-03-26 和 2024-11-05。

核心概念

服务端
显示服务端示例

服务端是你使用 MCP 协议的核心接口。它负责连接管理、协议合规和消息路由:

// Create a basic server
s := server.NewMCPServer(
    "My Server",  // Server name
    "1.0.0",     // Version
)

// Start the server using stdio
if err := server.ServeStdio(s); err != nil {
    log.Fatalf("Server error: %v", err)
}
### 资源
显示资源示例 资源是向大模型公开数据的方式。资源可以是任何内容,例如文件、API 响应、数据库查询、系统信息等。资源可以是:
  • 静态(固定 URI)
  • 动态(使用 URI 模板)

下面是一个简单的静态资源示例:

// Static resource example - exposing a README file
resource := mcp.NewResource(
    "docs://readme",
    "Project README",
    mcp.WithResourceDescription("The project's README file"), 
    mcp.WithMIMEType("text/markdown"),
)

// Add resource with its handler
s.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
    content, err := os.ReadFile("README.md")
    if err != nil {
        return nil, err
    }
    
    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      "docs://readme",
            MIMEType: "text/markdown",
            Text:     string(content),
        },
    }, nil
})

下面是一个使用模板的动态资源示例:

// 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
})

这些示例虽然简单,但展示了核心概念。资源可以复杂得多,例如提供多种内容、与数据库或外部 API 集成等。

工具
显示工具示例

工具允许大模型通过你的服务执行操作。与资源不同,工具预期会执行计算并产生副作用。它们类似于 REST API 中的 POST 端点。

任务增强型工具

任务增强型工具会异步执行,并通过轮询返回结果。这对于耗时较长、否则会阻塞或超时的操作很有用。任务工具支持三种模式:

  • TaskSupportForbidden(默认):不能将该工具作为任务调用
  • TaskSupportOptional:可以将该工具作为任务调用,也可以同步调用
  • TaskSupportRequired:必须将该工具作为任务调用
// Example: A tool that requires task execution
processBatchTool := mcp.NewTool("process_batch",
    mcp.WithDescription("Process a batch of items asynchronously"),
    mcp.WithTaskSupport(mcp.TaskSupportRequired),
    mcp.WithArray("items",
        mcp.Description("Array of items to process"),
        mcp.WithStringItems(),
        mcp.Required(),
    ),
)

// Task tool handler returns CreateTaskResult instead of CallToolResult
s.AddTaskTool(processBatchTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CreateTaskResult, error) {
    items := request.GetStringSlice("items", []string{})
    
    // Long-running work here
    for i, item := range items {
        select {
        case <-ctx.Done():
            // Task was cancelled
            return nil, ctx.Err()
        default:
            // Process item...
            processItem(item)
        }
    }
    
    // Return result - task ID and metadata are managed by the server
    return &mcp.CreateTaskResult{
        Task: mcp.Task{
            // Task fields (ID, status, etc.) are populated by the server
        },
    }, nil
})

// Enable task capabilities when creating the server
s := server.NewMCPServer(
    "Task Server",
    "1.0.0",
    server.WithTaskCapabilities(
        true, // listTasks: allows clients to list all tasks
        true, // cancel: allows clients to cancel running tasks
        true, // toolCallTasks: enables task augmentation for tools
    ),
    server.WithMaxConcurrentTasks(10), // Optional: limit concurrent running tasks
)

任务执行流程:

  1. 客户端在调用工具时传入任务参数
  2. 服务立即返回任务 ID
  3. 工具在后台异步执行
  4. 客户端轮询 tasks/result 以获取结果
  5. 完成后,服务发送任务状态通知

对于可选任务工具,可以同步调用同一工具(不带任务参数),也可以异步调用(带任务参数):

// Tool with optional task support
analyzeTool := mcp.NewTool("analyze_data",
    mcp.WithDescription("Analyze data - can run sync or async"),
    mcp.WithTaskSupport(mcp.TaskSupportOptional),
    mcp.WithString("data", mcp.Required()),
)

// Use AddTaskTool for hybrid tools that support both modes
s.AddTaskTool(analyzeTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CreateTaskResult, error) {
    // This handler runs when called as a task
    data := request.GetString("data", "")
    result := analyzeData(data)
    
    return &mcp.CreateTaskResult{
        Task: mcp.Task{},
    }, nil
})

// The server automatically handles both sync and async invocations
// When called without task param: executes handler and returns immediately
// When called with task param: executes handler asynchronously
限制并发任务数

为防止资源耗尽,你可以限制同时运行的任务数:

s := server.NewMCPServer(
    "Task Server",
    "1.0.0",
    server.WithTaskCapabilities(true, true, true),
    server.WithMaxConcurrentTasks(10), // Allow up to 10 concurrent running tasks
)

达到上限后,新的任务创建请求将失败并返回错误。已完成、失败或取消的任务不计入上限,只有处于“working”状态的任务才计入。如果未指定 WithMaxConcurrentTasks 或将其设为 0,则并发任务数不受限制。

对于执行后立即返回结果的传统同步工具:

简单计算示例:

calculatorTool := mcp.NewTool("calculate",
    mcp.WithDescription("Perform basic arithmetic calculations"),
    mcp.WithString("operation",
        mcp.Required(),
        mcp.Description("The arithmetic operation to perform"),
        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.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    args := request.GetArguments()
    op := args["operation"].(string)
    x := args["x"].(float64)
    y := args["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.FormatNumberResult(result), nil
})

HTTP 请求示例:

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) {
    args := request.GetArguments()
    method := args["method"].(string)
    url := args["url"].(string)
    body := ""
    if b, ok := args["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 mcp.NewToolResultErrorFromErr("unable to create request", err), nil
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return mcp.NewToolResultErrorFromErr("unable to execute request", err), nil
    }
    defer resp.Body.Close()

    // Return response
    respBody, err := io.ReadAll(resp.Body)
    if err != nil {
        return mcp.NewToolResultErrorFromErr("unable to read request response", err), nil
    }

    return mcp.NewToolResultText(fmt.Sprintf("Status: %d\nBody: %s", resp.StatusCode, string(respBody))), nil
})

工具可用于执行任何类型的计算或产生任何副作用:

  • 数据库查询
  • 文件操作
  • 外部 API 调用
  • 计算
  • 系统操作

每个工具都应:

  • 有清晰的描述
  • 验证输入
  • 妥善处理错误
  • 返回结构化响应
  • 使用合适的结果类型
提示词
显示提示词示例

提示词是可重复使用的模板,可帮助大模型有效地与你的服务交互。它们就像编码在服务中的“最佳实践”。以下是一些示例:

// 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.RoleUser,
                mcp.NewTextContent("Review the changes and provide constructive feedback."),
            ),
            mcp.NewPromptMessage(
                mcp.RoleAssistant,
                mcp.NewEmbeddedResource(mcp.ResourceContents{
                    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.RoleUser,
                mcp.NewTextContent("Help construct efficient and safe queries for the provided schema."),
            ),
            mcp.NewPromptMessage(
                mcp.RoleUser,
                mcp.NewEmbeddedResource(mcp.ResourceContents{
                    URI: fmt.Sprintf("db://schema/%s", tableName),
                    MIMEType: "application/json",
                }),
            ),
        },
    ), nil
})

提示词可以包含:

  • 系统指令
  • 必需参数
  • 嵌入的资源
  • 多条消息
  • 不同的内容类型(文本、图像等)
  • 自定义 URI 方案

示例

示例请参阅 examples/ 目录。

重要示例包括:

扩展内容

传输方式

MCP-Go 支持 stdio、SSE 和 streamable-HTTP 传输层。对于 SSE 传输,你可以使用 SetConnectionLostHandler() 检测并处理断开连接,以实现重连逻辑。

在非 net/http 框架中嵌入 StreamableHTTP

StreamableHTTPServer 是一个 http.Handler,因此可以挂载到任何使用 net/http 的路由器中。若要将其嵌入不使用 net/http 的框架(例如 fasthttp 或 fiber),且不通过适配器缓冲响应,请使用与传输无关的 Handle 入口:

func (s *StreamableHTTPServer) Handle(w HTTPResponseWriter, r *HTTPRequest)

HTTPRequest 是一个普通结构体(Method、URL、Header、Body、Context),而 HTTPResponseWriter 是一个小型接口(Header、WriteHeader、Write、Flush、CanStream)。底层传输无法流式传输的实现 MUST 从 CanStream 返回 false;此时服务端会以 405 Method Not Allowed 拒绝 GET(SSE 监听),并将 POST 响应保持为缓冲的 application/json,而不是升级为 text/event-stream。

完整的 fasthttp/fiber 适配器示例,请参阅 HTTP 传输文档。ServeHTTP 保持不变,仍是常规的 net/http 入口。

OAuth 受保护资源元数据

需要 OAuth 的服务端可通过 MCP 授权规范引用的 RFC 9728 /.well-known/oauth-protected-resource 端点公布其授权要求。使用 server.WithProtectedResourceMetadata(或 server.WithSSEProtectedResourceMetadata)自动挂载该端点,或者使用 server.NewProtectedResourceMetadataHandler 将其接入自定义路由器。示例请参阅 HTTP 传输文档。

httpServer := server.NewStreamableHTTPServer(mcpServer,
    server.WithProtectedResourceMetadata(server.ProtectedResourceMetadataConfig{
        Resource:             "https://my-mcp-server.com",
        AuthorizationServers: []string{"https://auth.example.com"},
        ScopesSupported:      []string{"mcp:read", "mcp:write"},
    }),
)
面向浏览器客户端的 CORS

向基于浏览器的 MCP 客户端开放的服务端,可选择在任一 HTTP 传输上启用跨源资源共享处理。默认情况下禁用 CORS;请通过 server.WithStreamableHTTPCORS 或 server.WithSSECORS 显式配置:

httpServer := server.NewStreamableHTTPServer(mcpServer,
    server.WithEndpointPath("/mcp"),
    server.WithStreamableHTTPCORS(
        server.WithCORSAllowedOrigins("https://my-ai-app.com", "http://localhost:3000"),
        server.WithCORSAllowCredentials(),
        server.WithCORSMaxAge(300),
    ),
)

传输会直接响应预检(OPTIONS)请求,并为简单响应添加相应的 Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Expose-Headers 和 Vary 标头。省略相应选项时会使用合理的默认值(方法为 GET, POST, DELETE, OPTIONS;请求标头为 Content-Type, Mcp-Session-Id, Last-Event-ID, Authorization;公开的标头为 Mcp-Session-Id)。将 WithCORSAllowedOrigins("*") 与 WithCORSAllowCredentials() 结合使用时,会回显请求中的 Origin,以保持符合规范。

localhost 服务端的 DNS 重绑定防护

两种 HTTP 传输都会自动保护本地服务端免受 DNS 重绑定攻击:通过环回连接(127.0.0.1、[::1])到达、且 Host 标头不是 localhost 值的请求,会被拒绝并返回 403 Forbidden。该检查会在运行时根据连接的本地地址进行,因此无论服务端监听的是 localhost 还是 0.0.0.0 都适用,并且绝不会影响通过非环回地址到达的请求。

如果同一主机上的反向代理通过 localhost 转发请求,同时保留原始 Host 标头,请将代理配置为把 Host 标头重写为 localhost,或显式选择退出:

httpServer := server.NewStreamableHTTPServer(mcpServer,
    // Or server.WithSSEDisableLocalhostProtection(true) on NewSSEServer.
    server.WithDisableLocalhostProtection(true),
)

详情请参阅 HTTP 传输文档,其中也说明了与框架无关的 Handle 入口的注意事项。

会话管理

MCP-Go 提供了可靠的会话管理系统,可帮助你:

  • 为每个连接的客户端维护独立状态
  • 注册并跟踪客户端会话
  • 向特定客户端发送通知
  • 为每个会话提供自定义工具
显示会话管理示例 #### 基本会话处理
// Create a server with session capabilities
s := server.NewMCPServer(
    "Session Demo",
    "1.0.0",
    server.WithToolCapabilities(true),
)

// Implement your own ClientSession
type MySession struct {
    id           string
    notifChannel chan mcp.JSONRPCNotification
    isInitialized bool
    // Add custom fields for your application
}

// Implement the ClientSession interface
func (s *MySession) SessionID() string {
    return s.id
}

func (s *MySession) NotificationChannel() chan<- mcp.JSONRPCNotification {
    return s.notifChannel
}

func (s *MySession) Initialize() {
    s.isInitialized = true
}

func (s *MySession) Initialized() bool {
    return s.isInitialized
}

// Register a session
session := &MySession{
    id:           "user-123",
    notifChannel: make(chan mcp.JSONRPCNotification, 10),
}
if err := s.RegisterSession(context.Background(), session); err != nil {
    log.Printf("Failed to register session: %v", err)
}

// Send notification to a specific client
err := s.SendNotificationToSpecificClient(
    session.SessionID(),
    "notification/update",
    map[string]any{"message": "New data available!"},
)
if err != nil {
    log.Printf("Failed to send notification: %v", err)
}

// Unregister session when done
s.UnregisterSession(context.Background(), session.SessionID())
每个会话的工具

对于更高级的用例,你可以实现 SessionWithTools 接口,以支持按会话自定义工具:

// Implement SessionWithTools interface for per-session tools
type MyAdvancedSession struct {
    MySession  // Embed the basic session
    sessionTools map[string]server.ServerTool
}

// Implement additional methods for SessionWithTools
func (s *MyAdvancedSession) GetSessionTools() map[string]server.ServerTool {
    return s.sessionTools
}

func (s *MyAdvancedSession) SetSessionTools(tools map[string]server.ServerTool) {
    s.sessionTools = tools
}

// Create and register a session with tools support
advSession := &MyAdvancedSession{
    MySession: MySession{
        id:           "user-456",
        notifChannel: make(chan mcp.JSONRPCNotification, 10),
    },
    sessionTools: make(map[string]server.ServerTool),
}
if err := s.RegisterSession(context.Background(), advSession); err != nil {
    log.Printf("Failed to register session: %v", err)
}

// Add session-specific tools
userSpecificTool := mcp.NewTool(
    "user_data",
    mcp.WithDescription("Access user-specific data"),
)
// You can use AddSessionTool (similar to AddTool)
err := s.AddSessionTool(
    advSession.SessionID(),
    userSpecificTool,
    func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        // This handler is only available to this specific session
        return mcp.NewToolResultText("User-specific data for " + advSession.SessionID()), nil
    },
)
if err != nil {
    log.Printf("Failed to add session tool: %v", err)
}

// Or use AddSessionTools directly with ServerTool
/*
err := s.AddSessionTools(
    advSession.SessionID(),
    server.ServerTool{
        Tool: userSpecificTool,
        Handler: func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
            // This handler is only available to this specific session
            return mcp.NewToolResultText("User-specific data for " + advSession.SessionID()), nil
        },
    },
)
if err != nil {
    log.Printf("Failed to add session tool: %v", err)
}
*/

// Delete session-specific tools when no longer needed
err = s.DeleteSessionTools(advSession.SessionID(), "user_data")
if err != nil {
    log.Printf("Failed to delete session tool: %v", err)
}
工具筛选

你还可以应用筛选器,控制特定会话可使用哪些工具:

// Add a tool filter that only shows tools with certain prefixes
s := server.NewMCPServer(
    "Tool Filtering Demo",
    "1.0.0",
    server.WithToolCapabilities(true),
    server.WithToolFilter(func(ctx context.Context, tools []mcp.Tool) []mcp.Tool {
        // Get session from context
        session := server.ClientSessionFromContext(ctx)
        if session == nil {
            return tools // Return all tools if no session
        }
        
        // Example: filter tools based on session ID prefix
        if strings.HasPrefix(session.SessionID(), "admin-") {
            // Admin users get all tools
            return tools
        } else {
            // Regular users only get tools with "public-" prefix
            var filteredTools []mcp.Tool
            for _, tool := range tools {
                if strings.HasPrefix(tool.Name, "public-") {
                    filteredTools = append(filteredTools, tool)
                }
            }
            return filteredTools
        }
    }),
)
使用上下文

会话上下文会自动传递给工具和资源处理程序:

s.AddTool(mcp.NewTool("session_aware"), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    // Get the current session from context
    session := server.ClientSessionFromContext(ctx)
    if session == nil {
        return mcp.NewToolResultError("No active session"), nil
    }
    
    return mcp.NewToolResultText("Hello, session " + session.SessionID()), nil
})

// When using handlers in HTTP/SSE servers, you need to pass the context with the session
httpHandler := func(w http.ResponseWriter, r *http.Request) {
    // Get session from somewhere (like a cookie or header)
    session := getSessionFromRequest(r)
    
    // Add session to context
    ctx := s.WithContext(r.Context(), session)
    
    // Use this context when handling requests
    // ...
}
请求钩子

创建一个 Hooks 对象,并从可用回调中选择所需项,即可接入请求生命周期。这样可以对所有功能进行遥测,并观察各种情况,例如统计格式不正确的请求,或在初始化期间记录 Agent 身份。

创建服务时,使用 server.WithHooks 选项将 Hooks 添加到服务中。

工具处理程序中间件

使用 server.WithToolHandlerMiddleware 选项为工具调用处理程序添加中间件。可以在创建服务时注册中间件,它们会应用于每次工具调用。

你可以使用恢复中间件选项,从工具调用中的 panic 中恢复,并通过 server.WithRecovery 选项将其添加到服务中。

提示词处理程序中间件

使用 server.WithPromptHandlerMiddleware 选项为提示词处理程序添加中间件。可以在创建服务时注册中间件,它们会应用于每次 prompts/get 调用。

提示词筛选

使用 server.WithPromptFilter 选项,根据上下文筛选提示词。其工作方式与工具筛选相同,但会应用于 prompts/list 结果。

重新生成服务端代码

服务端钩子和请求处理程序由生成器生成。运行以下命令即可重新生成:

go generate ./...

你需要安装 go,并确保 goimports 工具可用。生成器会自动运行 goimports 来格式化代码并修正导入项。

自动补全

当用户为特定提示词(由名称标识)或资源模板(由 URI 标识)填写参数值时,服务端可以提供上下文相关的建议。 若要启用补全支持,请在创建服务时使用 server.WithCompletions() 选项。

补全提供程序

你可以通过实现相应接口并将其作为选项传递给服务端,为提示词参数和资源模板参数提供补全逻辑。

显示补全提供程序示例
type MyPromptCompletionProvider struct{}

func (p *MyPromptCompletionProvider) CompletePromptArgument(
    ctx context.Context,
    promptName string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Example: provide style suggestions for a "code_review" prompt
    if promptName == "code_review" && argument.Name == "style" {
        styles := []string{"formal", "casual", "technical", "creative"}
        var suggestions []string
        
        // Filter based on current input
        for _, style := range styles {
            if strings.HasPrefix(style, argument.Value) {
                suggestions = append(suggestions, style)
            }
        }
        
        return &mcp.Completion{
            Values: suggestions,
        }, nil
    }
    
    // Return empty suggestions for unhandled cases
    return &mcp.Completion{Values: []string{}}, nil
}

type MyResourceCompletionProvider struct{}

func (p *MyResourceCompletionProvider) CompleteResourceArgument(
    ctx context.Context,
    uri string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Example: provide file path completions
    if uri == "file:///{path}" && argument.Name == "path" {
        // You can access previously completed arguments from context.Arguments
        // context.Arguments is a map[string]string of already-resolved arguments
        
        paths := getMatchingPaths(argument.Value) // Your custom logic
        
        return &mcp.Completion{
            Values:  paths[:min(len(paths), 100)], // Max 100 items
            Total:   len(paths),                    // Total available matches
            HasMore: len(paths) > 100,              // More results available
        }, nil
    }
    
    return &mcp.Completion{Values: []string{}}, nil
}

// Register the provider
mcpServer := server.NewMCPServer(
    "my-server",
    "1.0.0",
    server.WithCompletions(),
    server.WithPromptCompletionProvider(&MyPromptCompletionProvider{}),
    server.WithResourceCompletionProvider(&MyResourceCompletionProvider{}),
)
补全上下文

对于包含多个参数的提示词或资源模板,CompleteContext 参数可以访问先前已补全的参数。这样,你就可以根据先前的选择提供上下文相关的建议。

显示补全上下文示例
func (p *MyProvider) CompleteResourceArgument(
    ctx context.Context,
    uri string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Access previously completed arguments
    if previousValue, ok := context.Arguments["previous_arg"]; ok {
        // Provide suggestions based on previous_arg value
        return getSuggestionsFor(argument.Value, previousValue), nil
    }
    
    return &mcp.Completion{Values: []string{}}, nil
}
响应约束

返回补全结果时:

  • 每个响应最多包含 100 项
  • 使用 Total 表示可用匹配项的总数
  • 使用 HasMore 表示返回值之外是否还有更多结果

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。