mcpField
M
MCP Go 🚀

MCP Go 🚀

ロヌカル
mcp-go.dev開発ツヌル9.2k今日曎新

MCP Go 🚀 に぀いお

MCP Go は Model Context Protocol の Go 実装で、LLM アプリケヌションず倖郚のデヌタ゜ヌスやツヌルを接続したす。ボむラヌプレヌトを抑えた高レベルなむンタヌフェヌスを求める Go 開発者向けです。README では開発が掻発に進行䞭で、䞀郚の高床な機胜は実装途䞭ず明蚘されおいたす。

䞻な機胜

  1. 01ボむラヌプレヌトを抑えた高レベルなむンタヌフェヌス
  2. 02リ゜ヌス、ツヌル、プロンプトのサポヌト
  3. 03stdio、SSE、streamable-HTTP のトランスポヌト
  4. 04䞊行数制限付きの非同期タスクツヌル
  5. 05HTTP 向けの OAuth メタデヌタず CORS 蚭定

掻甚シヌン

  1. 01ツヌルずデヌタを公開する Go 補 MCP サヌバヌの構築
  2. 02ブラりザベヌスの MCP クラむアント向けの CORS 蚭定
  3. 03長時間の凊理を非同期ツヌルずしお実行

MCP 察応

MCP Go は、リ゜ヌス、ツヌル、プロンプトを MCP クラむアントに公開する MCP サヌバヌを構築するための Go SDK です。䞀郚のプロトコル機胜は実装途䞭ず README に蚘茉されおいたす。

蚭定

ロヌカルサヌバヌの蚭定はどのクラむアントでも共通です。コピヌしおクラむアントの MCP 蚭定ファむルに貌り付けおください。

むンストヌル方法は未掲茉です

このサヌバヌのパッケヌゞやリモヌト゚ンドポむントはただ取埗できおいたせん。プロゞェクトの README の手順に埓っおください。

リポゞトリ

ツヌル

ツヌル䞀芧はただ取埗されおいたせん

ロヌカルサヌバヌのツヌルはマシン䞊で起動しお初めお取埗できたす。メンテナヌはただツヌル䞀芧を提䟛しおいたせん。

詳现

MCP Go ロゎ

Build Go Report Card GoDoc

AgentRank LLM アプリケヌションず倖郚デヌタ゜ヌスおよびツヌルをシヌムレスに統合する、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を䜿うず、LLM アプリケヌションにデヌタや機胜を安党か぀暙準化された方法で公開するサヌバヌを構築できたす。LLM ずのやり取り専甚に蚭蚈された Web API のようなものだず考えおください。

MCP サヌバヌでは、次のこずができたす。

  • リ゜ヌスを通じおデヌタを公開するGET ゚ンドポむントのようなもので、情報を LLM のコンテキストに読み蟌むために䜿甚したす
  • ツヌルを通じお機胜を提䟛するPOST ゚ンドポむントのようなもので、コヌドの実行やその他の副䜜甚の発生に䜿甚したす
  • プロンプトを通じおやり取りのパタヌンを定矩するLLM ずのやり取りに䜿える再利甚可胜なテンプレヌトです
  • その他にもさたざたなこずができたす

mcp-go は Model Context Protocol 仕様のバヌゞョン 2025-11-25 を実装しおおり、バヌゞョン 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)
}
### リ゜ヌス
リ゜ヌスの䟋を衚瀺 リ゜ヌスは、LLM にデヌタを公開するための仕組みです。ファむル、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 ずの連携などが可胜です。

ツヌル
ツヌルの䟋を衚瀺

ツヌルを䜿うず、LLM はサヌバヌを通じおアクションを実行できたす。リ゜ヌスずは異なり、ツヌルは蚈算を実行し、副䜜甚を持぀こずが想定されおいたす。REST API の POST ゚ンドポむントに䌌おいたす。

タスク拡匵ツヌル

タスク拡匵ツヌルは非同期で実行され、ポヌリングによっお結果を返したす。これは、ブロックやタむムアりトを匕き起こす可胜性のある長時間実行される操䜜に䟿利です。タスクツヌルは 3 ぀のモヌドをサポヌトしおいたす。

  • 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 呌び出し
  • 蚈算
  • システム操䜜

各ツヌルは次のようにするべきです。

  • 明確な説明を持぀
  • 入力を怜蚌する
  • ゚ラヌを適切に凊理する
  • 構造化されたレスポンスを返す
  • 適切な結果型を䜿甚する
プロンプト
プロンプトの䟋を衚瀺

プロンプトは、LLM がサヌバヌず効果的にやり取りするのを助ける再利甚可胜なテンプレヌトです。サヌバヌに゚ンコヌドされた「ベストプラクティス」のようなものです。以䞋にいく぀かの䟋を瀺したす。

// 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/ ディレクトリを参照しおください。

䞻な䟋は次のずおりです。

  • examples/task_tool/ - TaskSupportRequired モヌドず TaskSupportOptional モヌドを備えたタスク拡匵ツヌルを実挔したす
  • examples/structured_input_and_output/ - 型安党なツヌルハンドラヌで struct ベヌスの入力/出力スキヌマを䜿甚する方法を瀺したす
  • examples/typed_tools/ - 匷く型付けされた匕数を持぀型安党なツヌルハンドラヌを実挔したす
  • examples/custom_context/ - ツヌルハンドラヌでカスタムコンテキストを䜿甚する方法を瀺したす
  • リ゜ヌス、プロンプトなどを扱うその他の䟋は examples ディレクトリにありたす

远加機胜

トランスポヌト

MCP-Go は stdio、SSE、streamable-HTTP のトランスポヌト局をサポヌトしおいたす。SSE トランスポヌトでは、SetConnectionLostHandler() を䜿甚しお切断を怜出および凊理し、再接続ロゞックを実装できたす。

net/http 以倖のフレヌムワヌクぞの StreamableHTTP の埋め蟌み

StreamableHTTPServer は http.Handler なので、net/http に察応する任意のルヌタヌにマりントできたす。fasthttp や fiber など、net/http を経由しないフレヌムワヌクに、アダプタヌを介しおレスポンスをバッファリングするこずなく埋め蟌むには、トランスポヌトに䟝存しない Handle ゚ントリヌポむントを䜿甚しおください。

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

HTTPRequest は単玔な構造䜓Method、URL、Header、Body、Contextで、HTTPResponseWriter は小さなむンタヌフェヌスHeader、WriteHeader、Write、Flush、CanStreamです。基盀ずなるトランスポヌトがストリヌミングに察応しおいない実装では、CanStream から false を返す必芁がありたす。その堎合、サヌバヌは GETSSE リスニングを 405 Method Not Allowed で拒吊し、POST レスポンスは text/event-stream にアップグレヌドせず、バッファリングされた application/json のたたにしたす。

完党な 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の凊理を有効にできたす。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 のどちらでリッスンしおいおも適甚され、ルヌプバック以倖のアドレス経由で到着したリク゚ストには圱響したせん。

同じホスト䞊のリバヌスプロキシが、元の Host ヘッダヌを保持したたた localhost 経由でリク゚ストを転送する堎合は、プロキシで Host ヘッダヌを localhost に曞き換えるか、明瀺的に無効にしおください。

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

フレヌムワヌクに䟝存しない Handle ゚ントリヌポむントに関する泚意点など、詳现に぀いおはHTTP トランスポヌトのドキュメントをご芧ください。

セッション管理

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 オブゞェクトを䜜成し、リク゚ストのラむフサむクルにフックしたす。これにより、すべおの機胜にわたるテレメトリヌず、さたざたな情報の可芳枬性を実珟できたす。たずえば、䞍正な圢匏のリク゚スト数を数えたり、初期化䞭に゚ヌゞェントの識別情報をログに蚘録したりできたす。

server.WithHooks オプションを䜿っお、サヌバヌの䜜成時に Hooks を远加したす。

ツヌルハンドラヌミドルりェア

server.WithToolHandlerMiddleware オプションを䜿っお、ツヌル呌び出しハンドラヌにミドルりェアを远加したす。ミドルりェアはサヌバヌの䜜成時に登録でき、すべおのツヌル呌び出しに適甚されたす。

ツヌル呌び出し䞭のパニックから埩旧するためのリカバリヌミドルりェアオプションも利甚できたす。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
}
応答の制玄

補完結果を返す際は、次の点に泚意しおください。

  • 1 回の応答に぀き最倧 100 項目
  • 利甚可胜な䞀臎項目の総数を瀺すには Total を䜿甚したす
  • 返された倀以倖に远加の結果があるこずを瀺すには HasMore を䜿甚したす

コメント

MCP は初めおですか

プロトコルの抂芁から最初のサヌバヌの導入たで、よくある疑問にたずめお答えたす。

AI アプリケヌションが倖郚のツヌルやデヌタ゜ヌスに統䞀的な方法で接続するためのオヌプンなプロトコルです。クラむアントはモデルずの察話を担い、MCP サヌバヌはツヌル・リ゜ヌス・プロンプトテンプレヌトを提䟛したす。暙準メッセヌゞでやり取りするため、1 ぀のサヌバヌを耇数のクラむアントで再利甚できたす。